/*
**	$Id: termBuffer.c,v 1.12 92/08/15 20:13:37 olsen Sta Locker: olsen $
**	$Revision: 1.12 $
**	$Date: 92/08/15 20:13:37 $
**
**	Support routines for the log book (review buffer)
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* A handy macro to determine the length of a string. */

#define LINE_WIDTH(Line) (((ULONG *)BufferLines[Line])[-1])

	/* Some private data (render info & window). */

STATIC struct Window	*BufferWindow;
STATIC struct Screen	*BufferScreen;

STATIC struct RastPort	*BPort;
STATIC APTR		 BufferVisualInfo;

STATIC LONG		 BufLine,BufCols;

STATIC BYTE		 BufferTerminated;

STATIC WORD		 LocalUserFontWidth,LocalUserFontHeight,LocalUserFontBase,
			 LocalTextFontWidth,LocalTextFontHeight,LocalTextFontBase;

STATIC struct TextAttr	 LocalUserFont;
STATIC UBYTE		 LocalUserFontName[256];

STATIC struct TextAttr	 LocalTextFont;
STATIC UBYTE		 LocalTextFontName[256];

STATIC LONG		 CurrentLine,DisplayedLines;

enum	{	MEN_SEARCH,MEN_REPEAT,MEN_GOTO,MEN_CLEARBUF,MEN_CLOSEBUF,MEN_QUITBUF };

enum	{	GAD_SCROLL };

STATIC struct NewMenu BufferMenu[] =
{
	{ NM_TITLE, NULL,		 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_SEARCH},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_REPEAT},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_GOTO},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_CLEARBUF},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_CLOSEBUF},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_QUITBUF},
	{ NM_END, 0,			 0 , 0, 0, (APTR)0}
};

	/* LaunchBuffer():
	 *
	 *	Launch the buffer process.
	 */

BYTE
LaunchBuffer()
{
		/* Is the buffer process already running? */

	if(BufferProcess)
	{
			/* Tell it to bring its screen to the front. */

		Signal(BufferProcess,SIGBREAKF_CTRL_D);

			/* Return success. */

		return(TRUE);
	}
	else
	{
		struct MsgPort	*ReplyPort;
		BYTE		 Result = FALSE;

			/* Create reply port. */

		if(ReplyPort = CreateMsgPort())
		{
			struct Message *Message;

				/* Create startup message. */

			if(Message = (struct Message *)AllocVec(sizeof(struct Message),MEMF_ANY|MEMF_CLEAR))
			{
				Message -> mn_ReplyPort	= ReplyPort;
				Message -> mn_Length	= sizeof(struct Message);

					/* Launc the buffer process. */

				if(BufferProcess = (struct Process *)CreateNewProcTags(
					NP_Entry,	BufferServer,
					NP_Name,	"term buffer process",
					NP_Priority,	0,
					NP_StackSize,	8192,
					NP_WindowPtr,	-1,
				TAG_END))
				{
						/* Send the startup message. */

					PutMsg(&BufferProcess -> pr_MsgPort,Message);

						/* Wait for reply. */

					WaitPort(ReplyPort);

						/* Pick up the reply. */

					GetMsg(ReplyPort);

						/* Is the process still running? */

					if(BufferProcess)
						Result = TRUE;
				}

					/* Free the startup message. */

				FreeVec(Message);
			}

				/* Delete the reply port. */

			DeleteMsgPort(ReplyPort);
		}

			/* Return the result. */

		return(Result);
	}
}

	/* AllocString(STRPTR String,WORD Len):
	 *
	 +	Allocate space for a string.
	 */

STATIC STRPTR __regargs
AllocString(STRPTR String,LONG Len)
{
	ULONG *Mem;

	if(Mem = AllocMem(sizeof(ULONG) + Len + 1,MEMF_ANY | MEMF_CLEAR))
	{
		*Mem++ = Len;

		CopyMem(String,Mem,Len);

		return((STRPTR)Mem);
	}
	else
		return(NULL);
}

	/* FreeString(STRPTR String):
	 *
	 *	Free the space occupied by a string.
	 */

STATIC VOID __regargs
FreeString(STRPTR String)
{
	FreeMem(&((ULONG *)String)[-1],((ULONG *)String)[-1] + sizeof(ULONG) + 1);
}

	/* AddLine(STRPTR Line,LONG Size):
	 *
	 *	Add a line to the display buffer.
	 */

VOID
AddLine(STRPTR Line,LONG Size)
{
	if(!BufferClosed)
	{
		ULONG Signals = 0;

			/* Is the buffer array initialized? */

		if(BufferLines)
		{
				/* Pick up the global access semaphore
				 * (two tasks are sharing the data).
				 */

			ObtainSemaphore(BufferSemaphore);

				/* Check for limit. */

			if(Config . MaxLogBuffSize && BufferSpace >= Config . MaxLogBuffSize)
				goto Wrap;

				/* We've reached the last line in the buffer. */

			if(Lines == MaxLines)
			{
				STRPTR	*MoreBuffer;
				LONG	 i;

					/* Allocate space for some more lines. */

				if(MoreBuffer = (STRPTR *)AllocVec((MaxLines + 100) * sizeof(STRPTR),MEMF_ANY | MEMF_CLEAR))
				{
						/* Copy the old lines to the new
						 * buffer.
						 */

					for(i = 0 ; i < Lines ; i++)
						MoreBuffer[i] = BufferLines[i];

						/* Free the old lines. */

					FreeVec(BufferLines);

						/* Set the new buffer. */

					MaxLines += 100;

					BufferLines = MoreBuffer;
				}
				else
				{
						/* We couldn't get enough memory
						 * to extend the number of lines
						 * in the buffer, so we'll have
						 * to wrap the contents of the
						 * buffer around.
						 */

					if(Lines)
					{
Wrap:						BufferSpace -= ((ULONG *)BufferLines[0])[-1];

						FreeString(BufferLines[0]);

						for(i = 1 ; i < MaxLines ; i++)
							BufferLines[i - 1] = BufferLines[i];

						Lines--;

							/* Tell the buffer task to
							 * refresh the display.
							 */

						Signals = SIGBREAKF_CTRL_F;
					}
				}
			}

				/* Allocate a new line and copy the buffer contents
				 * into it.
				 */

			if(BufferLines[Lines] = AllocString(Line,Size))
			{
				Lines++;

				BufferSpace += Size;
			}

			ReleaseSemaphore(BufferSemaphore);

				/* Tell the buffer task to update the display. */

			if(!Signals)
			{
				Signals = SIGBREAKF_CTRL_E;

				UpdateReview(FALSE);
			}
			else
				UpdateReview(TRUE);

			if(BufferProcess)
				Signal(BufferProcess,Signals);
		}
	}
}

	/* ClearBuffer():
	 *
	 *	Clear the current display buffer and flush the contents
	 *	of the buffered capture file handles.
	 */

VOID
ClearBuffer()
{
	LONG i;

	if(BufferProcess)
	{
		Signal(BufferProcess,SIGBREAKF_CTRL_C);

		Wait(SIGBREAKF_CTRL_C);
	}

	if(PrinterCapture)
		Flush(PrinterCapture);

		/* Free the contents of the display buffer. */

	if(BufferLines)
	{
		for(i = 0 ; i < Lines ; i++)
		{
			if(BufferLines[i])
				FreeString(BufferLines[i]);
		}

		FreeVec(BufferLines);

		BufferLines = NULL;

		Lines = 0;

		MaxLines = 100;

		BufferLines = (STRPTR *)AllocVec(MaxLines * sizeof(STRPTR),MEMF_ANY|MEMF_CLEAR);
	}

	BufferSpace = 0;
}

	/* StoreBuffer(APTR Buffer,LONG Size):
	 *
	 *	Store data in the display buffer.
	 */

VOID
StoreBuffer(APTR Buffer,LONG Size)
{
	STATIC UBYTE	 LineBuffer[200];
	STATIC LONG 	 BufferCount = 0;

	UBYTE		*CharBuffer = Buffer;
	LONG		 i;

	for(i = 0 ; i < Size ; i++)
	{
			/* Look which char we are to handle. */

		switch(CharBuffer[i])
		{
				/* Ignore the following characters. */

			case FFD:
			case BEL:
			case DEL:
			case VTB:
			case XON:
			case XOF:	continue;

				/* Move the cursor to the next tab
				 * stop.
				 */

			case TAB:	if(BufferCount + 8 < LastColumn)
					{
						strcpy(&LineBuffer[BufferCount],"        ");

						BufferCount += 8;

						break;
					}

					continue;

				/* Move the cursor one step back. */

			case BKS:	if(BufferCount)
						BufferCount--;

					continue;

				/* Ignore carriage return (should have been
				 * filtered already).
				 */

			case RET:	continue;

				/* Terminate the current line. */

			case ENT:	AddLine(LineBuffer,BufferCount);

					BufferCount = 0;
					continue;

				/* Stuff the character into the buffer. */

			default:	LineBuffer[BufferCount++] = CharBuffer[i];
					break;
		}

			/* The line is full, add it to the display buffer. */

		if(BufferCount > LastColumn || BufferCount == sizeof(LineBuffer))
		{
			AddLine(LineBuffer,BufferCount);

			BufferCount = 0;
		}
	}
}

	/* FlushIMsg(struct Window *Window):
	 *
	 +	Clear the MsgPort of a window.
	 */

STATIC VOID
FlushIMsg(struct Window *Window)
{
	struct IntuiMessage *Massage;

	while(Massage = GT_GetIMsg(Window -> UserPort))
		GT_ReplyIMsg(Massage);
}

	/* CreateAllGadgets():
	 *
	 *	Create all the gadgets required by the
	 *	buffer screen (i.e. the scroller gadget).
	 */

STATIC struct Gadget *
CreateAllGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;

	memset(&NewGadget,0,sizeof(struct NewGadget));

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_GadgetText	= "";
		NewGadget . ng_Width		= LocalUserFontWidth * 2;
		NewGadget . ng_Height		= BufferWindow -> Height;
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= BufferVisualInfo;
		NewGadget . ng_GadgetID		= 0;
		NewGadget . ng_LeftEdge		= BufferWindow -> Width - LocalUserFontWidth * 2;
		NewGadget . ng_TopEdge		= 0;

		GadgetArray[0] = Gadget = CreateGadget(SCROLLER_KIND,Gadget,&NewGadget,
			GTSC_Arrows,	LocalUserFontHeight,
			PGA_Freedom,	LORIENT_VERT,
		TAG_DONE);
	}

	return(Gadget);
}

	/* GetSearchString(STRPTR Buffer):
	 *
	 *	Prompt user for a string to search the buffer for.
	 */

STATIC BYTE
GetSearchString(STRPTR Buffer)
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[4];
	struct Window	*PanelWindow;
	UBYTE		 OtherBuffer[256];
	BYTE		 Success = FALSE;

	strcpy(OtherBuffer,Buffer);

	if(CreateAllGetsGadgets(FALSE,OtherBuffer,LocaleString(MSG_TERMBUFFER_ENTER_SEARCH_STRING_TXT),&GadgetArray[0],&GadgetList,BufferVisualInfo,BufferScreen -> WBorTop + BufferScreen -> Font -> ta_YSize + 1,BufferScreen))
	{
		if(PanelWindow = OpenWindowTags(NULL,
			WA_Left,		(BufferScreen -> Width	- SZ_GetWindowWidth())	/ 2,
			WA_Top,			(BufferScreen -> Height	- SZ_GetWindowHeight())	/ 2,
			WA_Width,		SZ_GetWindowWidth(),
			WA_Height,		SZ_GetWindowHeight(),

			WA_Activate,		TRUE,
			WA_DragBar,		TRUE,
			WA_DepthGadget,		TRUE,
			WA_CloseGadget,		TRUE,
			WA_RMBTrap,		TRUE,
			WA_CustomScreen,	BufferScreen,

			WA_IDCMP,		IDCMP_ACTIVEWINDOW | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_GADGETDOWN | BUTTONIDCMP | STRINGIDCMP,

			WA_Title,		LocaleString(MSG_TERMBUFFER_ENTER_SEARCH_STRING_TXT),
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;
			ULONG			 SignalSet;

			AddGList(PanelWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,PanelWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(PanelWindow,NULL);

			ActiveGadget = NULL;

			while(!Terminated)
			{
				SignalSet = Wait((1 << PanelWindow -> UserPort -> mp_SigBit) | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_C);

				if(SignalSet & SIGBREAKF_CTRL_D)
					BumpWindow(PanelWindow);

				if(SignalSet & SIGBREAKF_CTRL_C)
				{
					BufferTerminated = TRUE;

					Terminated = TRUE;
				}

				if(SignalSet & (1 << PanelWindow -> UserPort -> mp_SigBit))
				{
					while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(PanelWindow -> UserPort)))
					{
						Class	= Massage -> Class;
						Code	= Massage -> Code;
						Gadget	= (struct Gadget *)Massage -> IAddress;

						GT_ReplyIMsg(Massage);

						if(Class == IDCMP_VANILLAKEY)
							KeySelect(GadgetArray,3,Code,PanelWindow,&Gadget,&Class,&Code);

						if(Class == IDCMP_GADGETDOWN)
						{
							if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
								ActiveGadget = Gadget;
						}

						if(Class == IDCMP_ACTIVEWINDOW && ActiveGadget)
							ActivateGadget(ActiveGadget,PanelWindow,NULL);

						if(Class == IDCMP_CLOSEWINDOW)
							Terminated = TRUE;

						if(Class == IDCMP_GADGETUP)
						{
							switch(Gadget -> GadgetID)
							{
								case 0: if(Code != '\t' && ((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> Buffer[0])
									{
										strcpy(Buffer,GT_STRING(GadgetArray[0]));

										Success = TRUE;

										Terminated = TRUE;
									}

									break;

								case 1:	if(((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> Buffer[0])
									{
										strcpy(Buffer,GT_STRING(GadgetArray[0]));

										Success = TRUE;

										Terminated = TRUE;
									}

									break;

								case 3:	Terminated = TRUE;
									break;
							}
						}
					}
				}
			}

			RemoveGList(PanelWindow,GadgetList,(UWORD)-1);

			CloseWindow(PanelWindow);
		}
	}

	FreeGadgets(GadgetList);

	return(Success);
}

	/* PrintLine(STRPTR Buffer,LONG LineNumber):
	 *
	 *	Print a line at a given line number in the displayed area.
	 */

STATIC VOID
PrintLine(STRPTR Buffer,LONG LineNumber)
{
	WORD Length = ((ULONG *)Buffer)[-1];

		/* The line doesn't exactly fill the displayed line,
		 * so erase the remaining columns.
		 */

	if(Length < BufCols)
		ClipBlit(BPort,0,0,BPort,Length * LocalTextFontWidth,LineNumber * LocalTextFontHeight,(BufCols - Length) * LocalTextFontWidth,LocalTextFontHeight,0x00);

		/* Print the text. */

	if(Length)
	{
		Move(BPort,0,(LineNumber * LocalTextFontHeight) + LocalTextFontBase);

		if(Length > BufCols)
			Text(BPort,Buffer,BufCols);
		else
			Text(BPort,Buffer,Length);
	}
}

	/* RedrawScreen(LONG FirstLine):
	 *
	 *	Redraw the contents of the entire screen and return the
	 *	number of lines actually drawn.
	 */

STATIC LONG
RedrawScreen(LONG FirstLine)
{
	LONG i,Last,Line = 0;

		/* Determine last line to display. */

	if((Last = FirstLine + BufLine) >= Lines)
		Last = Lines;

		/* Obtain the access semaphore and display the single
		 * lines.
		 */

	ObtainSemaphore(BufferSemaphore);

	for(i = FirstLine ; i < Last ; i++)
		PrintLine(BufferLines[i],Line++);

	ReleaseSemaphore(BufferSemaphore);

		/* We didn't fill the whole screen, so clear the rest. */

	if(Line < BufLine)
		ClipBlit(BPort,0,0,BPort,0,Line * LocalTextFontHeight,BufCols * LocalTextFontWidth,(BufLine - Line) * LocalTextFontHeight,0x00);

	return(Line);
}

	/* MarkArea(WORD Column,WORD Line,WORD Length):
	 *
	 *	Mark an area in the term Buffer window.
	 */

STATIC VOID
MarkArea(WORD Column,WORD Line,WORD Length)
{
	STATIC WORD OldColumn = -1,OldLine = -1,OldLength = -1;

	if(OldColumn != -1)
		ClipBlit(BPort,0,0,BPort,OldColumn * LocalTextFontWidth,OldLine * LocalTextFontHeight,OldLength * LocalTextFontWidth,LocalTextFontHeight,0x50);

	if(OldColumn != Column || OldLine != Line || OldLength != Length)
	{
		if(Column != -1)
			ClipBlit(BPort,0,0,BPort,Column * LocalTextFontWidth,Line * LocalTextFontHeight,Length * LocalTextFontWidth,LocalTextFontHeight,0x50);
	}

	OldColumn	= Column;
	OldLine		= Line;
	OldLength	= Length;
}

	/* Search():
	 *
	 *	String search function, based on the Boyer-Moore search
	 *	algorithm.
	 */

STATIC LONG
Search(STRPTR Pattern,LONG *FoundX,LONG *FoundY,LONG *SaveX)
{
	UBYTE	Distance[256],NewPattern[256],PatternWidth,SearchWidth;
	LONG	LineNumber = -1,i;
	WORD	j,k,l;

		/* Determine length of search pattern. */

	PatternWidth = strlen(Pattern);

		/* Convert search pattern to upper cse. */

	for(i = 0 ; i <= PatternWidth ; i++)
		NewPattern[i] = ToUpper(Pattern[i]);

		/* Create distance table. */

	for(i = 0 ; i < 256 ; i++)
		Distance[i] = PatternWidth;

	for(i = 0 ; i < PatternWidth - 1 ; i++)
		Distance[NewPattern[i]] = PatternWidth - i - 1;

		/* Run down the buffer. */

	for(i = *FoundY ; i < Lines ; i++)
	{
			/* Is there anything to search for? */

		if(SearchWidth = ((ULONG *)BufferLines[i])[-1])
		{
				/* Casteth the magick spelle of
				 * Boyer-Moore...
				 */

			j = PatternWidth;

			do
			{
				k = PatternWidth;
				l = j;

				do
				{
					k--;
					l--;
				}
				while(k >= 0 && NewPattern[k] == ToUpper(BufferLines[i][l]));

				j += Distance[ToUpper(BufferLines[i][j - 1])];
			}
			while(k >= 0 && j <= SearchWidth);

				/* Found first occurence, set up the
				 * data and quit.
				 */

			if(k < 0)
			{
				*FoundX	= l + 1;
				*FoundY	= LineNumber = i;

				*SaveX	= l + 1;

				if(BufferLines[i][*FoundX + 1])
					*FoundX++;
				else
				{
					if(*FoundY - 1 < Lines)
					{
						*FoundX = 0;
						*FoundY++;
					}
				}

				return(LineNumber);
			}
			else
				*FoundX = 0;
		}
	}

	return(LineNumber);
}

STATIC VOID __regargs
BufferClipPage(struct BlockMarker *Marker)
{
	struct IFFHandle *Handle;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = (ULONG)OpenClipboard(Config . ClipboardUnit))
		{
			InitIFFasClip(Handle);
	
			if(!OpenIFF(Handle,IFFF_WRITE))
			{
				if(!PushChunk(Handle,'FTXT','FORM',IFFSIZE_UNKNOWN))
				{
					if(!PushChunk(Handle,0,'CHRS',IFFSIZE_UNKNOWN))
					{
						WORD	Lines = Marker -> LastLine - Marker -> FirstLine - 1,
							i;

						if(LINE_WIDTH(Marker -> FirstLine) > Marker -> FirstColumn)
							WriteTrimmedString(Handle,&BufferLines[Marker -> FirstLine][Marker -> FirstColumn],LINE_WIDTH(Marker -> FirstLine) - Marker -> FirstColumn);

						WriteChunkBytes(Handle,"\n",1);

						if(Lines > 0)
						{
							for(i = 0 ; i < Lines ; i++)
							{
								if(LINE_WIDTH(Marker -> FirstLine + 1 + i))
									WriteTrimmedString(Handle,BufferLines[Marker -> FirstLine + 1 + i],LINE_WIDTH(Marker -> FirstLine + 1 + i));

								WriteChunkBytes(Handle,"\n",1);
							}
						}

						if(Marker -> LastColumn > LINE_WIDTH(Marker -> LastLine))
							WriteTrimmedString(Handle,BufferLines[Marker -> LastLine],LINE_WIDTH(Marker -> LastLine));
						else
							WriteTrimmedString(Handle,BufferLines[Marker -> LastLine],Marker -> LastColumn);

						WriteChunkBytes(Handle,"\n",1);

						PopChunk(Handle);
					}

					PopChunk(Handle);
				}

				CloseIFF(Handle);
			}

			CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}
}

STATIC VOID
BufferClip(struct Gadget *Scroller)
{
	struct BlockMarker	*Marker;
	WORD			 FirstX = BufferWindow -> MouseX / LocalTextFontWidth,
				 FirstY = BufferWindow -> MouseY / LocalTextFontHeight;

	if(Marker = BM_SetMark(BufferWindow -> RPort,ToggleSelect,ToggleSelect,BufCols,BufLine,CurrentLine,Lines,BufferWindow -> MouseX / LocalTextFontWidth,BufferWindow -> MouseY / LocalTextFontHeight,LocalTextFontWidth,LocalTextFontHeight))
	{
		struct IntuiMessage	*Massage;
		ULONG			 Code,Class;
		BYTE			 Done = FALSE,Xerox = FALSE,HasMouse = FALSE;
		WORD			 PlusX = LocalTextFontWidth - 1,
					 MouseX,MouseY,
					 Delta = 0;

		while(!Done)
		{
			WaitPort(BufferWindow -> UserPort);

			while(Massage = GT_GetIMsg(BufferWindow -> UserPort))
			{
				Class	= Massage -> Class;
				Code	= Massage -> Code;
				MouseX	= Massage -> MouseX;
				MouseY	= Massage -> MouseY;

				GT_ReplyIMsg(Massage);

				if(Class == IDCMP_INTUITICKS && Delta != 0)
				{
					if(!HasMouse)
					{
						SetMarker(BufferWindow);

						HasMouse = TRUE;
					}

					if((Delta > 0 && CurrentLine + BufLine < Lines) || (Delta < 0 && CurrentLine > 0))
					{
						if(Delta < 0)
						{
							if(DisplayedLines)
								ScrollRaster(BPort,0,-LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

							PrintLine(BufferLines[--CurrentLine],0);
						}
						else
						{
							if(DisplayedLines)
								ScrollRaster(BPort,0,LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

							PrintLine(BufferLines[CurrentLine + BufLine],BufLine - 1);

							CurrentLine++;
						}

						Marker -> Top += Delta;
						Marker -> LastY -= Delta;

						BM_ExtendMark(Marker,(MouseX + PlusX) / LocalTextFontWidth,MouseY / LocalTextFontHeight,Delta);
					}
					else
						Delta = 0;
				}

				if(Class == IDCMP_MOUSEBUTTONS && (Code & IECODE_UP_PREFIX))
				{
					BM_Draw(Marker,Marker -> Unselect);

					if(Code == MIDDLEUP)
						Xerox = TRUE;

					Done = TRUE;

					break;
				}

				if(Class == IDCMP_MOUSEMOVE)
				{
					if(!HasMouse)
					{
						SetMarker(BufferWindow);

						HasMouse = TRUE;
					}

					BM_ExtendMark(Marker,(MouseX + PlusX) / LocalTextFontWidth,MouseY / LocalTextFontHeight,0);

					if(MouseY < 1)
					{
						if(CurrentLine > 0)
							Delta = -1;
					}
					else
					{
						if(MouseY >= BufferWindow -> Height - 1 && CurrentLine + BufLine < Lines)
							Delta = 1;
					}

					while(Delta)
					{
						MouseX	= Window -> MouseX;
						MouseY	= Window -> MouseY;

						if((Delta < 0 && MouseY > 0) || (Delta > 0 && MouseY < BufferWindow -> Height - 1))
							break;
						else
						{
							if((Delta > 0 && CurrentLine + BufLine < Lines) || (Delta < 0 && CurrentLine > 0))
							{
								if(Delta < 0)
								{
									if(DisplayedLines)
										ScrollRaster(BPort,0,-LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

									PrintLine(BufferLines[--CurrentLine],0);
								}
								else
								{
									if(DisplayedLines)
										ScrollRaster(BPort,0,LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

									PrintLine(BufferLines[CurrentLine + BufLine],BufLine - 1);

									CurrentLine++;
								}

								Marker -> Top	+= Delta;
								Marker -> LastY	-= Delta;

								BM_ExtendMark(Marker,(MouseX + PlusX) / LocalTextFontWidth,MouseY / LocalTextFontHeight,Delta);

								GT_SetGadgetAttrs(Scroller,BufferWindow,NULL,
									GTSC_Top,CurrentLine,
								TAG_DONE);
							}
							else
								break;
						}
					}

					Delta = 0;
				}
			}
		}

		if(FirstX != BufferWindow -> MouseX / LocalTextFontWidth || FirstY != BufferWindow -> MouseY / LocalTextFontHeight)
		{
			if(Marker -> FirstColumn == Marker -> Width)
			{
				Marker -> FirstLine++;

				Marker -> FirstColumn = 0;
			}

			if(Marker -> LastColumn == 0)
			{
				Marker -> LastLine--;

				Marker -> LastColumn = Marker -> Width;
			}

			if(Marker -> FirstLine <= Marker -> LastLine)
			{
				if(Marker -> FirstLine != Marker -> LastLine || Marker -> FirstColumn != Marker -> LastColumn)
				{
					SetWait(BufferWindow);

					if(Marker -> FirstLine == Marker -> LastLine)
						SaveClip(&BufferLines[Marker -> FirstLine][Marker -> FirstColumn],Marker -> LastColumn - Marker -> FirstColumn);
					else
						BufferClipPage(Marker);
				}
			}
		}

		ClearPointer(BufferWindow);

		FreeVec(Marker);
	}
}

	/* BufferServer():
	 *
	 *	Asynchronous task to display the data stored in the
	 *	scrollback display buffer.
	 */

VOID __saveds
BufferServer()
{
	struct ColorSpec	 ColorSpec[3];

	ULONG			 SignalSet;

	struct IntuiMessage	*Massage;
	ULONG			 Class,Code,Qualifier;
	UBYTE			 Char,LastChar = 0;

	LONG			 MouseX,MouseY;

	UBYTE			 PercentBuffer[80];

	struct Menu		*BufferMenuStrip;

	STATIC UBYTE		 SearchBuffer[256];
	LONG			 LineNumber,SaveX,FoundX = 0,FoundY = 0,Width;
	ULONG			 DisplayMode;

	struct Rectangle	 DisplayClip;

	UWORD			 SomeColour;

	struct Gadget		*GadgetList = NULL;
	struct Gadget		*GadgetArray[1];

	struct TextFont		*LocalFont;

	struct Message		*StartupMessage;
	struct MsgPort		*StartupPort = &((struct Process *)SysBase -> ThisTask) -> pr_MsgPort;

	WaitPort(StartupPort);

	StartupMessage = GetMsg(StartupPort);

	LocalTextFontWidth	= TextFontWidth;
	LocalTextFontHeight	= TextFontHeight;
	LocalTextFontBase	= TextFontBase;

	LocalUserFontWidth	= UserFontWidth;
	LocalUserFontHeight	= UserFontHeight;
	LocalUserFontBase	= UserFontBase;

	memcpy(&LocalUserFont,&UserFont,sizeof(struct TextAttr));

	LocalUserFont . ta_Name = LocalUserFontName;

	strcpy(LocalUserFontName,UserFont . ta_Name);

	memcpy(&LocalTextFont,&TextAttr,sizeof(struct TextAttr));

	LocalTextFont . ta_Name = LocalTextFontName;

	strcpy(LocalTextFontName,TextAttr . ta_Name);

	BufferTerminated = FALSE;

		/* Set up the startup colours for our buffer screen. */

	ColorSpec[0] . ColorIndex	= 0;
	ColorSpec[0] . Red		= (Config . Colours[0] >> 8) & 0xF;
	ColorSpec[0] . Green		= (Config . Colours[0] >> 4) & 0xF;
	ColorSpec[0] . Blue		= (Config . Colours[0]     ) & 0xF;

	switch(Config . ColourMode)
	{
		case COLOUR_EIGHT:	SomeColour = Config . Colours[7];
					break;

		case COLOUR_SIXTEEN:	SomeColour = Config . Colours[15];
					break;

		case COLOUR_AMIGA:
		default:		SomeColour = Config . Colours[1];
					break;
	}

	ColorSpec[1] . ColorIndex	= 1;
	ColorSpec[1] . Red		= (SomeColour >> 8) & 0xF;
	ColorSpec[1] . Green		= (SomeColour >> 4) & 0xF;
	ColorSpec[1] . Blue		= (SomeColour     ) & 0xF;

	ColorSpec[2] . ColorIndex	= -1;

		/* We'll use a fixed screen width, only the
		 * height is adapted from the main screen.
		 */

	DisplayMode = Config . DisplayMode;

	if(LocalFont = OpenFont(&LocalTextFont))
	{
			/* Inquire the text overscan dimensions. */

		if(QueryOverscan(DisplayMode,&DisplayClip,OSCAN_TEXT))
		{
			UWORD Height = (Screen -> Height - (Screen -> BarHeight + 2)) / LocalUserFontHeight,CharCount;

			Width = Screen -> Width - 2 * LocalUserFontWidth;

			if((CharCount = Width / LocalTextFontWidth) > 80)
				CharCount = 80;

			Width = CharCount * LocalTextFontWidth + 2 * LocalUserFontWidth;

				/* Centre the buffer screen. */

			if(DisplayClip . MaxX - DisplayClip . MinX + 1 > Width)
			{
				LONG Differ = ((DisplayClip . MaxX - DisplayClip . MinX + 1) - (Width)) / 2;

				DisplayClip . MinX += Differ;
				DisplayClip . MaxX -= Differ;
			}

				/* Open a single bitplane clone of the main screen. */

			if(BufferScreen = (struct Screen *)OpenScreenTags(NULL,
				SA_Title,	LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT),
				SA_Depth,	1,
				SA_DClip,	&DisplayClip,
				SA_Height,	Height * LocalUserFontHeight + Screen -> BarHeight + 2,
				SA_DisplayID,	DisplayMode,
				SA_Font,	&LocalUserFont,
				SA_Behind,	TRUE,
				SA_AutoScroll,	TRUE,
				SA_Colors,	ColorSpec,
			TAG_END))
			{
				if(BufferVisualInfo = GetVisualInfo(BufferScreen,TAG_DONE))
				{
					LocalizeMenu(BufferMenu,MSG_TERMBUFFER_PROJECT_MEN);

					if(BufferMenuStrip = CreateMenus(BufferMenu,TAG_DONE))
					{
						if(LayoutMenus(BufferMenuStrip,BufferVisualInfo,
							GTMN_TextAttr,		&LocalUserFont,
							GTMN_NewLookMenus,	TRUE,
						TAG_DONE))
						{
							WORD TitleOffset;

							TitleOffset = (strlen(LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT)) + 1) * LocalUserFontWidth + 4;

								/* Open a cute window on our buffer screen. */

							if(BufferWindow = OpenWindowTags(NULL,
								WA_Top,		BufferScreen -> BarHeight + 2,
								WA_Left,	0,
								WA_Width,	BufferScreen -> Width,
								WA_Height,	BufferScreen -> Height - (Screen -> BarHeight + 2),
								WA_Backdrop,	TRUE,
								WA_Borderless,	TRUE,
								WA_SmartRefresh,FALSE,
								WA_CustomScreen,BufferScreen,
								WA_RMBTrap,	TRUE,
								WA_NewLookMenus,TRUE,
								WA_RptQueue,	1,
								WA_IDCMP,	IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_MENUPICK | ARROWIDCMP | SCROLLERIDCMP,
							TAG_DONE))
							{
								if(CreateAllGadgets(&GadgetArray[0],&GadgetList))
								{
										/* Signal our father process that
										 * we're running.
										 */

									ReplyMsg(StartupMessage);

									StartupMessage = NULL;

									SetMenuStrip(BufferWindow,BufferMenuStrip);

									AddGList(BufferWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
									RefreshGList(GadgetList,BufferWindow,NULL,(UWORD)-1);
									GT_RefreshWindow(BufferWindow,NULL);

Restart:								BPort = BufferWindow -> RPort;

									SetFont(BPort,LocalFont);

										/* Determine maximum dimensions of
										 * the buffer screen (in rows and
										 * columns).
										 */

									BufCols	= (BufferWindow -> Width - 16) / LocalTextFontWidth;
									BufLine	= BufferWindow -> Height / LocalTextFontHeight;

										/* Bring the screen to the front. */

									BumpWindow(BufferWindow);

										/* Set the drawing pens for the window. */

									SetAPen(BPort,1);
									SetBPen(BPort,0);
									SetDrMd(BPort,JAM2);

										/* Initial creation of the buffer display. */

									DisplayedLines = RedrawScreen(CurrentLine = 0);

									GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
										GTSC_Top,	0,
										GTSC_Total,	Lines,
										GTSC_Visible,	BufLine,
									TAG_DONE);

									BufferWindow -> Flags &= ~WFLG_RMBTRAP;

									do
									{
											/* Show where we are. */

										if(Lines)
										{
											extern APTR LocaleBase;

											SetAPen(BufferScreen -> BarLayer -> rp,0);
											SetBPen(BufferScreen -> BarLayer -> rp,1);
											SetDrMd(BufferScreen -> BarLayer -> rp,JAM2);

											if(LocaleBase)
												SPrintf(PercentBuffer,"%lD/%lD (%ld%%)                  ",CurrentLine,Lines > BufLine ? Lines - BufLine : 0,(100 * (CurrentLine + DisplayedLines)) / Lines);
											else
												SPrintf(PercentBuffer,"%ld/%ld (%ld%%)                  ",CurrentLine,Lines > BufLine ? Lines - BufLine : 0,(100 * (CurrentLine + DisplayedLines)) / Lines);

											Move(BufferScreen -> BarLayer -> rp,TitleOffset,LocalUserFontBase + 1);
											Text(BufferScreen -> BarLayer -> rp,PercentBuffer,strlen(PercentBuffer));
										}

										SignalSet = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F | (1 << BufferWindow -> UserPort -> mp_SigBit));

											/* Leave the town? */

										if(SignalSet & SIGBREAKF_CTRL_C)
											BufferTerminated = TRUE;

											/* Bring our window to the front. */

										if(SignalSet & SIGBREAKF_CTRL_D)
											BumpWindow(BufferWindow);

											/* We've got one more line in the
											 * buffer.
											 */

										if(SignalSet & SIGBREAKF_CTRL_E)
										{
											if(Lines - CurrentLine > DisplayedLines && DisplayedLines < BufLine)
											{
												ObtainSemaphore(BufferSemaphore);

												PrintLine(BufferLines[CurrentLine + DisplayedLines],DisplayedLines++);

												ReleaseSemaphore(BufferSemaphore);
											}

											GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
												GTSC_Total,	Lines,
												GTSC_Visible,	BufLine,
											TAG_DONE);
										}

											/* The contents of the buffer have moved
											 * up a line.
											 */

										if(SignalSet & SIGBREAKF_CTRL_F)
											DisplayedLines = RedrawScreen(CurrentLine);

											/* Process the incoming window
											 * input.
											 */

										while(Massage = (struct IntuiMessage *)GT_GetIMsg(BufferWindow -> UserPort))
										{
											Class		= Massage -> Class;
											Code		= Massage -> Code;
											Qualifier	= Massage -> Qualifier;
											MouseX		= Massage -> MouseX;
											MouseY		= Massage -> MouseY;

												/* This hack is necessary to obtain the
												 * character codes generated for the cursor
												 * keys. A control or alternate qualifier
												 * would spoil the result (i.e. we would
												 * not get a valid key code).
												 */

											Massage -> Qualifier = NULL;

											Char = KeyConvert(Massage,NULL,NULL);

												/* Just in case anybody needs it... */

											Massage -> Qualifier = Qualifier;

											GT_ReplyIMsg(Massage);

											if(Class == IDCMP_MOUSEMOVE)
											{
												if(Code != CurrentLine)
												{
													if(ABS(CurrentLine - Code) == 1)
													{
														Qualifier	= NULL;
														Class		= IDCMP_RAWKEY;

														if(Code > CurrentLine)
															Code = CDN;
														else
															Code = CUP;

														goto UseKey;
													}
													else
													{
														MarkArea(-1,-1,-1);

														DisplayedLines = RedrawScreen(CurrentLine = Code);
													}
												}
											}

											if(Class == IDCMP_RAWKEY)
											{
												if(LastChar && (Code & IECODE_UP_PREFIX))
												{
													GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
														GTSC_Top,	CurrentLine,
														GTSC_Total,	Lines,
														GTSC_Visible,	BufLine,
													TAG_DONE);
												}

												if(LastChar = Char)
												{
														/* Use the numeric keypad keys to
														 * move through the buffer.
														 */

UseKey:													if(Qualifier & IEQUALIFIER_NUMERICPAD)
													{
															/* Remove the numpad qualifier. */

														Qualifier &= ~IEQUALIFIER_NUMERICPAD;

														switch(Char - '0')
														{
																/* Jump to bottom. */

															case 1: Char = CDN;
																Qualifier |= IEQUALIFIER_CONTROL;
																break;

																/* Jump to top. */

															case 7: Char = CUP;
																Qualifier |= IEQUALIFIER_CONTROL;
																break;

																/* Move one page down. */

															case 3: Char = CDN;
																Qualifier |= IEQUALIFIER_LSHIFT;
																break;

																/* Move one page up. */

															case 9: Char = CUP;
																Qualifier |= IEQUALIFIER_LSHIFT;
																break;

																/* Move one line down. */

															case 2: Char = CDN;
																break;

																/* Move one line up. */

															case 8: Char = CUP;
																break;
														}
													}

														/* Check cursor keys. */

													switch(Char)
													{
															/* Scroll the buffer up. */

														case CUP:	if(Qualifier & IEQUALIFIER_CONTROL)
																{
																	MarkArea(-1,-1,-1);

																	if(CurrentLine)
																	{
																		DisplayedLines = RedrawScreen(CurrentLine = 0);

																		GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																			GTSC_Top,	0,
																			GTSC_Total,	Lines,
																			GTSC_Visible,	BufLine,
																		TAG_DONE);
																	}

																	break;
																}

																if(Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
																{
																	LONG NewCurrentLine;

																	if((NewCurrentLine = CurrentLine - BufLine) < 0)
																		NewCurrentLine = 0;

																	MarkArea(-1,-1,-1);

																	if(NewCurrentLine != CurrentLine)
																	{
																		DisplayedLines = RedrawScreen(CurrentLine = NewCurrentLine);

																		GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																			GTSC_Top,	CurrentLine,
																			GTSC_Total,	Lines,
																			GTSC_Visible,	BufLine,
																		TAG_DONE);
																	}

																	break;
																}

																if(CurrentLine)
																{
																	MarkArea(-1,-1,-1);

																	if(DisplayedLines)
																		ScrollRaster(BPort,0,-LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

																	CurrentLine--;

																	ObtainSemaphore(BufferSemaphore);

																	PrintLine(BufferLines[CurrentLine],0);

																	ReleaseSemaphore(BufferSemaphore);

																	if(DisplayedLines < BufLine)
																		DisplayedLines++;
																}

																break;

															/* Scroll the buffer down. */

														case CDN:	if(Qualifier & IEQUALIFIER_CONTROL)
																{
																	LONG NewCurrentLine;

																	if((NewCurrentLine = Lines - BufLine) < 0)
																		NewCurrentLine = 0;

																	MarkArea(-1,-1,-1);

																	if(CurrentLine != NewCurrentLine)
																	{
																		DisplayedLines = RedrawScreen(CurrentLine = NewCurrentLine);

																		GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																			GTSC_Top,	CurrentLine,
																			GTSC_Total,	Lines,
																			GTSC_Visible,	BufLine,
																		TAG_DONE);
																	}

																	break;
																}

																if(Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
																{
																	LONG NewCurrentLine;

																	if((NewCurrentLine = CurrentLine + (2 * BufLine)) > Lines)
																		NewCurrentLine = Lines;

																	if((NewCurrentLine = NewCurrentLine - BufLine) < 0)
																		NewCurrentLine = 0;

																	MarkArea(-1,-1,-1);

																	if(NewCurrentLine != CurrentLine)
																	{
																		DisplayedLines = RedrawScreen(CurrentLine = NewCurrentLine);

																		GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																			GTSC_Top,	CurrentLine,
																			GTSC_Total,	Lines,
																			GTSC_Visible,	BufLine,
																		TAG_DONE);
																	}

																	break;
																}

																if(CurrentLine + BufLine < Lines)
																{
																	MarkArea(-1,-1,-1);

																	if(DisplayedLines)
																		ScrollRaster(BPort,0,LocalTextFontHeight,0,0,(BufCols * LocalTextFontWidth) - 1,(DisplayedLines * LocalTextFontHeight) - 1);

																	ObtainSemaphore(BufferSemaphore);

																	PrintLine(BufferLines[CurrentLine + BufLine],BufLine - 1);

																	ReleaseSemaphore(BufferSemaphore);

																	CurrentLine++;

																	if(DisplayedLines < BufLine)
																		DisplayedLines++;
																}

																break;

														default:	break;
													}
												}

												continue;
											}

												/* User hit a mouse button. */

											if(Class == IDCMP_MOUSEBUTTONS && !(Code & IECODE_UP_PREFIX))
											{
												MarkArea(-1,-1,-1);

													/* Reasonable dimensions? */

												if(MouseY / LocalTextFontHeight < DisplayedLines && MouseX / LocalTextFontWidth < BufCols)
												{
													ReportMouse(TRUE,BufferWindow);

													ObtainSemaphore(BufferSemaphore);

													BufferClip(GadgetArray[GAD_SCROLL]);

													ReleaseSemaphore(BufferSemaphore);

													ReportMouse(FALSE,BufferWindow);

													while(Massage = (struct IntuiMessage *)GT_GetIMsg(BufferWindow -> UserPort))
														GT_ReplyIMsg(Massage);

													if(Code == MIDDLEDOWN || (Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)))
														Signal(ThisProcess,SIG_CLIP);

													break;
												}
											}

											if(Class == IDCMP_MENUPICK)
											{
												struct MenuItem *MenuItem;

												while(Code != MENUNULL)
												{
													MenuItem = ItemAddress(BufferMenuStrip,Code);

													switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
													{
														case MEN_SEARCH:	BlockWindow(BufferWindow);

GetTheString:																if(Lines)
																	{
																		if(GetSearchString(SearchBuffer))
																		{
																			FoundX = FoundY = 0;

SearchForIt:																	LineNumber = -1;

																			if(FoundY == 0 && CurrentLine != 0)
																				FoundY = CurrentLine;

																			ObtainSemaphore(BufferSemaphore);

																			LineNumber = Search(SearchBuffer,&FoundX,&FoundY,&SaveX);

																			ReleaseSemaphore(BufferSemaphore);

																			if(LineNumber == -1)
																			{
																				BlockWindows();

																				MyEasyRequest(BufferWindow,LocaleString(MSG_TERMBUFFER_DID_NOT_FIND_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SearchBuffer);

																				ReleaseWindows();

																				FlushIMsg(BufferWindow);

																				FoundX = FoundY = 0;

																				MarkArea(-1,-1,-1);
																			}
																			else
																			{
																				if(LineNumber < CurrentLine)
																				{
																					MarkArea(-1,-1,-1);

																					DisplayedLines = RedrawScreen(CurrentLine = 0);

																					GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																						GTSC_Top,	CurrentLine,
																						GTSC_Total,	Lines,
																						GTSC_Visible,	BufLine,
																					TAG_DONE);
																				}
																				else
																				{
																					if(LineNumber > CurrentLine + DisplayedLines - 1)
																					{
																						MarkArea(-1,-1,-1);

																						if(LineNumber >= Lines - BufLine)
																						{
																							LONG NewCurrentLine;

																							if((NewCurrentLine = Lines - BufLine) < 0)
																								NewCurrentLine = 0;

																							if(CurrentLine != NewCurrentLine)
																							{
																								DisplayedLines = RedrawScreen(CurrentLine = NewCurrentLine);

																								GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																									GTSC_Top,	CurrentLine,
																									GTSC_Total,	Lines,
																									GTSC_Visible,	BufLine,
																								TAG_DONE);
																							}
																						}
																						else
																						{
																							DisplayedLines = RedrawScreen(CurrentLine = LineNumber);

																							GT_SetGadgetAttrs(GadgetArray[GAD_SCROLL],BufferWindow,NULL,
																								GTSC_Top,	CurrentLine,
																								GTSC_Total,	Lines,
																								GTSC_Visible,	BufLine,
																							TAG_DONE);
																						}
																					}
																				}

																				MarkArea(SaveX,LineNumber - CurrentLine,strlen(SearchBuffer));
																			}

																			FoundY = (FoundY + 1) % Lines;
																		}
																	}
																	else
																		MyEasyRequest(BufferWindow,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

																	ReleaseWindow(BufferWindow);
																	break;

														case MEN_REPEAT:	BlockWindow(BufferWindow);

																	if(!SearchBuffer[0])
																		goto GetTheString;
																	else
																		goto SearchForIt;

														case MEN_GOTO:		BumpWindow(TopWindow);
																	break;

														case MEN_QUITBUF:
														case MEN_CLOSEBUF:	BufferTerminated = TRUE;
																	break;

														case MEN_CLEARBUF:	if(Lines)
																	{
																		BlockWindows();

																		BlockWindow(BufferWindow);

																		if(Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
																		{
																			ClearBuffer();

																			ReleaseWindow(BufferWindow);

																			ReleaseWindows();

																			FlushIMsg(BufferWindow);

																			goto Restart;
																		}
																		else
																		{
																			if(MyEasyRequest(BufferWindow,LocaleString(MSG_TERMBUFFER_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Lines))
																			{
																				ClearBuffer();

																				ReleaseWindow(BufferWindow);

																				ReleaseWindows();

																				FlushIMsg(BufferWindow);

																				goto Restart;
																			}
																		}

																		ReleaseWindow(BufferWindow);

																		ReleaseWindows();

																		FlushIMsg(BufferWindow);
																	}

																	break;

														default:		break;
													}

													Code = MenuItem -> NextSelect;
												}
											}

										}
									}
									while(!BufferTerminated);

									RemoveGList(BufferWindow,GadgetList,(UWORD)-1);
								}

								FreeGadgets(GadgetList);

								BumpWindow(TopWindow);

								MarkArea(-1,-1,-1);

								ScreenToBack(BufferScreen);

								BufferWindow -> Flags |= WFLG_RMBTRAP;

								ClearMenuStrip(BufferWindow);

								CloseWindow(BufferWindow);
							}
						}

						FreeMenus(BufferMenuStrip);
					}

					FreeVisualInfo(BufferVisualInfo);
				}

				CloseScreen(BufferScreen);
			}
		}

		CloseFont(LocalFont);
	}

	Forbid();

	BufferProcess = NULL;

	if(StartupMessage)
		ReplyMsg(StartupMessage);
	else
		Signal(ThisProcess,SIGBREAKF_CTRL_C);
}
