/* TextReader by Oren Peli, Copyright (c) 1987 BazboSOFT! */

/*
 *	Introduction:
 *	
 *		TextReader is, I believe, the most useful program I've written on
 *			the Amiga and I am very proud of it.
 *		
 *		TextReader's purpose is to make editing within the CLI much easier,
 *			and as far as I know, there isn't any other program of that kind
 *			for the Amiga.
 *		
 *		Examples of where could TextReader make you life easier:
 *		
 *		    1)	Right after finishing writing TextReader, I wanted to copy it
 *				to my browsing/debugging/working CLI disk, only that
 *				it was completely full.
 *				
 *			I dir'ed the C subdirectory and saw many files I never
 *				use (lab, if, endif, else, etc.). So I wrote (from csh):
 *			% rm
 *				and with TextReader copied all the files names I wanted
 *				to delete.
 *				
 *		    2)	While looking at several Fish disks to see how useful and
 *		    	debugged TextReader was, I've encounterred STEmualtor:
 *		    	I cd'ed into it's directory (with TextReader, of
 *				course) and 'Type'ed 'README'. Fish wrote in that file
 *				that in order to run the STEmulator, it is necessary to
 *		   		write something like:
 *				"assign STEmulator: AmigaLibDisk43:STEmulator",
 *				so, with TextReader, I simply copied those instructions
 *				instead of retyping then.
 *		
 *	Usage:
 *	
 *		It's very simple: run it (using the run command) and press the
 *			left mouse button within the CLI window on a text, drag the
 *			mouse and watch the results.
 *		
 *		To quit:	You have a menu: just press the right mouse button
 *				and pick the Quit (and only) menu.
 *	
 *	
 *		Note:	The menu will appear ONLY if the pointer is positioned
 *			above the window's title bar.
 *		
 *	
 *	If the right mouse button is pressed:
 *	
 *			If the pointer is positioned left of the cursor (but in the
 *				same line) the program will generate BACKSPACE events.
 *		
 *			If the pointer is positioned right of the cursor (but in the
 *				same line) the program will generate DEL keys events.
 *		
 *			If the pointer is positioned exactly over the cursor, the
 *				program will echo your arguments.
 *			
 *			If the pointer is positioned under the current cursor line,
 *				the program will generate a RETURN key event.
 *				
 *	Syntax:
 *		RUN TextReader [string...]
 *		
 *		Note: For generating Return key event, use '\n'
 *		
 *	Examples:
 *		
 *		RUN TextReader
 *		RUN TextReader "Cd df1:\nDir opt a\n"
 *		RUN TextReader sys:
 *		RUN TextReader NewCLI\n
 *		
 *	Problems list:
 *		1)	The program won't recognize alt characters.
 *		2)	The program won't recognize any text written with foreground
 *				color other then 1 and/or background color other then 0.
 *		3)	The program won't recognize any text written in any other
 *				style then normal.
 *		4)	The program won't recognize any text written with font
 *				which its size is not 8x8 (the font must also be
 *				a fixed with font (e.g. not proportional) ).
 *		5)	After changing the font (using 'font' for example) of the cli,
 *				you MUST quit from TextReader and re-run it.
 *	
 *	All in all, TextReader is an extremely useful utility and should be called
 *		from everyone's startup-sequence.
 *	
 *	Bug reports, suggestions, etc. can be sent to me at the following address:
 *		
 *		Oren Peli
 *		Shkediya 13
 *		Ramat-Gan	52-546
 *		ISRAEL
 *		
 *	..or, if you want to chat with me over the phone, you can call: 03-7514475.
 *	
 *	
 *	You may not immediately realize how powerful TextReader really is,
 *		after doing so, please consider sending me a donation
 *		($20 seems like a nice round number), although TextReader is NOT
 *		a Sharewere program but a public domain.
 *
 *	For every $100 dollars I receive, I will fix a problem from
 *		the above list.
 *	
 *	ENJOY!
 */

#include <intuition/intuitionbase.h>	/* Added by Adams Douglas */
#include <devices/conunit.h>
#include <devices/input.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <libraries/dosextens.h>

extern void *AllocMem();

extern PLANEPTR AllocRaster();

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;

extern void *OpenLibrary();

extern void *CreateStdIO();

extern long findWindow();

struct ConUnit *conUnit = NULL;

struct Window *conWindow = NULL;

void *CreatePort() , *FindPort();

struct Process *FindTask();

struct IntuiMessage *GetMsg();

struct RastPort dummy_rastport , *trp = &dummy_rastport;
struct BitMap dummy_bitmap , *tbm = &dummy_bitmap;

struct RastPort dummy_rastport2 , *crp = &dummy_rastport2;
struct BitMap dummy_bitmap2 , *cbm = &dummy_bitmap2;


struct IntuiText ItemText = { 3 , 1 , JAM1 , 1 , 1 , NULL , (STRPTR) "Quit TextReader"
, NULL };

struct MenuItem MenuItem = { NULL , 0 , 0 , 121 , 10 , ITEMTEXT | ITEMENABLED |
HIGHCOMP, 0, (APTR) &ItemText , NULL , NULL , NULL , 0xFFFF };

struct Menu Menu = { NULL , 0 , 0 , 121 , 0 , MENUENABLED , " Quit" , &MenuItem };

struct chr_data
{
	char chr;
	char raw_data;
	char is_shifted;
	PLANEPTR pln;
};

struct chr_data chrs[] =
{
	{	' '	,	0x40	,	0	} ,
	{	'a'	,	0x20	,	0	} ,
	{	'b'	,	0x35	,	0	} ,
	{	'c'	,	0x33	,	0	} ,
	{	'd'	,	0x22	,	0	} ,
	{	'e'	,	0x12	,	0	} ,
	{	'f'	,	0x23	,	0	} ,
	{	'g'	,	0x24	,	0	} ,
	{	'h'	,	0x25	,	0	} ,
	{	'i'	,	0x17	,	0	} ,
	{	'j'	,	0x26	,	0	} ,
	{	'k'	,	0x27	,	0	} ,
	{	'l'	,	0x28	,	0	} ,
	{	'm'	,	0x37	,	0	} ,
	{	'n'	,	0x36	,	0	} ,
	{	'o'	,	0x18	,	0	} ,
	{	'p'	,	0x19	,	0	} ,
	{	'q'	,	0x10	,	0	} ,
	{	'r'	,	0x13	,	0	} ,
	{	's'	,	0x21	,	0	} ,
	{	't'	,	0x14	,	0	} ,
	{	'u'	,	0x16	,	0	} ,
	{	'v'	,	0x34	,	0	} ,
	{	'w'	,	0x11	,	0	} ,
	{	'x'	,	0x32	,	0	} ,
	{	'y'	,	0x15	,	0	} ,
	{	'z'	,	0x31	,	0	} ,
	{	'0'	,	0x0a	,	0	} ,
	{	'1'	,	0x01	,	0	} ,
	{	'2'	,	0x02	,	0	} ,
	{	'3'	,	0x03	,	0	} ,
	{	'4'	,	0x04	,	0	} ,
	{	'5'	,	0x05	,	0	} ,
	{	'6'	,	0x06	,	0	} ,
	{	'7'	,	0x07	,	0	} ,
	{	'8'	,	0x08	,	0	} ,
	{	'9'	,	0x09	,	0	} ,
	{	'-'	,	0x0b	,	0	} ,
	{	'='	,	0x0c	,	0	} ,
	{	'\\'	,	0x0d	,	0	} ,
	{	'['	,	0x1a	,	0	} ,
	{	']'	,	0x1b	,	0	} ,
	{	';'	,	0x29	,	0	} ,
	{	'\''	,	0x2a	,	0	} ,
	{	','	,	0x38	,	0	} ,
	{	'.'	,	0x39	,	0	} ,
	{	'/'	,	0x3a	,	0	} ,
	{	'`'	,	0x00	,	0	} ,
	{	'A'	,	0x20	,	1	} ,
	{	'B'	,	0x35	,	1	} ,
	{	'C'	,	0x33	,	1	} ,
	{	'D'	,	0x22	,	1	} ,
	{	'E'	,	0x12	,	1	} ,
	{	'F'	,	0x23	,	1	} ,
	{	'G'	,	0x24	,	1	} ,
	{	'H'	,	0x25	,	1	} ,
	{	'I'	,	0x17	,	1	} ,
	{	'J'	,	0x26	,	1	} ,
	{	'K'	,	0x27	,	1	} ,
	{	'L'	,	0x28	,	1	} ,
	{	'M'	,	0x37	,	1	} ,
	{	'N'	,	0x36	,	1	} ,
	{	'O'	,	0x18	,	1	} ,
	{	'P'	,	0x19	,	1	} ,
	{	'Q'	,	0x10	,	1	} ,
	{	'R'	,	0x13	,	1	} ,
	{	'S'	,	0x21	,	1	} ,
	{	'T'	,	0x14	,	1	} ,
	{	'U'	,	0x16	,	1	} ,
	{	'V'	,	0x34	,	1	} ,
	{	'W'	,	0x11	,	1	} ,
	{	'X'	,	0x32	,	1	} ,
	{	'Y'	,	0x15	,	1	} ,
	{	'Z'	,	0x31	,	1	} ,
	{	'!'	,	0x01	,	1	} ,
	{	'@'	,	0x02	,	1	} ,
	{	'#'	,	0x03	,	1	} ,
	{	'$'	,	0x04	,	1	} ,
	{	'%'	,	0x05	,	1	} ,
	{	'^'	,	0x06	,	1	} ,
	{	'&'	,	0x07	,	1	} ,
	{	'*'	,	0x08	,	1	} ,
	{	'('	,	0x09	,	1	} ,
	{	')'	,	0x0a	,	1	} ,
	{	'_'	,	0x0b	,	1	} ,
	{	'+'	,	0x0c	,	1	} ,
	{	'|'	,	0x0d	,	1	} ,
	{	'{'	,	0x1a	,	1	} ,
	{	'}'	,	0x1b	,	1	} ,
	{	':'	,	0x29	,	1	} ,
	{	'"'	,	0x2a	,	1	} ,
	{	'<'	,	0x38	,	1	} ,
	{	'>'	,	0x39	,	1	} ,
	{	'?'	,	0x3a	,	1	} ,
	{	'~'	,	0x00	,	1	} ,
	{	-1	,	-1	,	-1	}
};

struct MsgPort *port = NULL;
struct IOStdReq *req = NULL;

long errdev = 0;

int num_of_chrs;

struct InputEvent ie;

int really_open_window = 0;

do_args(c , v)
register int c;
register char *v[];
{
	register int i , j , k , s;
	register char chr;
	
	for (i = 1 ; i < c ; i++)
	{
		s = strlen(v[i]);

		for (j = 0 ; j < s ; j++)
		{
			if (v[i][j] == '\\' && v[i][j + 1] == 'n')
			{
				ie.ie_Qualifier = NULL;

				ie.ie_Code = 0x44;

				DoIO(req);

				ie.ie_Code = 0x44 | 0x80;

				DoIO(req);

				j += 2;
			}

			chr = v[i][j];

			for (k = 0 ; k != num_of_chrs ; k++)
				if (chrs[k].chr == chr)
				{
					fake_raw_key_event(k);
					
					break;
				}
		}
	}
}

fake_raw_key_event(c)
register char c;
{
	ie.ie_Qualifier = chrs[c].is_shifted ? IEQUALIFIER_LSHIFT : NULL;
	ie.ie_Code = chrs[c].raw_data;

	DoIO(req);

	ie.ie_Code |= 0x80;

	DoIO(req);
}

the_same()
{
	register int i;

	register PLANEPTR ptr1 , ptr2;

	ptr1 = cbm->Planes[0];
	ptr2 = tbm->Planes[0];

	for (i = 0 ; i != 16 ; i += 2)
		if (ptr1[i] != ptr2[i])
			return(0);

	return(1);
}

identify_char()
{
	register int c;

	for (c = 0 ; c != num_of_chrs ; c++)
	{
		cbm->Planes[0] = chrs[c].pln;

		if (the_same() )
			return(c);
	}

	return(-1);
}

convert_char(x , y)
register int x , y;
{
	register int rx , ry;

	register int c;

	rx = x - conUnit->cu_XROrigin;
	ry = y - conUnit->cu_YROrigin;

	rx &= ~7;
	ry &= ~7;

	rx += conUnit->cu_XROrigin;
	ry += conUnit->cu_YROrigin;

	ClipBlit(conWindow->RPort , (long) rx , (long) ry , trp , 0L , 0L , 8L , 8L ,
										0xC0L);

	if ( (c = identify_char() ) != -1)
		fake_raw_key_event(c);
}

main(argc , argv)
int argc;
char *argv[];
{
	register struct Window *win;
	register struct IntuiMessage *msg;
	register int x , y , code , qua;
	register long class;
	register int pressed = 0;
	register int rx , ry;
	register int prx = -1111 , fy , px = -1111 , maxx = -1111;
	register int i;
	register char buf[2];
	register long bl;
	register char string_buffer[50];

	puts("TextReader, by Oren Peli, Copyright (C) 1987 BazboSOFT!");

	for (i = 0 ; chrs[i].raw_data != -1 ; i++)
		chrs[i].pln = NULL;

	num_of_chrs = i - 1;

	if (NOT (IntuitionBase = (struct IntuitionBase *)
					OpenLibrary("intuition.library" , 33L) ) )
		bye();

	if (NOT findWindow() )
		bye();

	win = conWindow;

	sprintf(string_buffer , "TextReader's port (win:%lx)" , win);

	if (FindPort(string_buffer) )
	{
		puts("But you have already ran TextReader from this window!");

		bye();
	}

	if (NOT (port = CreatePort(string_buffer , 0L) ) )
		bye();

	if (NOT (req = CreateStdIO(port) ) )
		bye();

	if (errdev = OpenDevice("input.device" , 0L , req , 0L) )
		bye();

	if (NOT (GfxBase = (struct GfxBase *) OpenLibrary("graphics.library" , 33L) ) )
		bye();

	InitRastPort(trp);
	InitRastPort(crp);

	InitBitMap(tbm , 1L , 8L , 8L);
	InitBitMap(cbm , 1L , 8L , 8L);

	trp->BitMap = tbm;
	crp->BitMap = cbm;

	if (NOT (tbm->Planes[0] = AllocRaster(8L , 8L) ) )
		bye();

	SetAPen(trp , 0L);

	RectFill(trp , 0L , 0L , 8L , 8L);

	buf[1] = 0;

	SetAPen(crp , 1L);
	SetDrMd(crp , JAM2);

	bl = GfxBase->DefaultFont->tf_Baseline;

	for (i = 0 ; i != num_of_chrs ; i++)
	{
		cbm->Planes[0] = chrs[i].pln = AllocRaster(8L , 8L);

		if (NOT chrs[i].pln)
			bye();

		Move(crp , 0L , bl);

		buf[0] = chrs[i].chr;

		Text(crp , buf , 1L);
	}

	req->io_Command = IND_WRITEEVENT;
	req->io_Flags = 0;
	req->io_Length = (long) sizeof(struct InputEvent);
	req->io_Data = (void *) &ie;

	ie.ie_NextEvent = NULL;
	ie.ie_Class = IECLASS_RAWKEY;
	ie.ie_TimeStamp.tv_secs = 0;
	ie.ie_TimeStamp.tv_micro = 0;
	ie.ie_X = 0;
	ie.ie_Y = 0;

	ModifyIDCMP(win , MOUSEBUTTONS | MOUSEMOVE | MENUPICK);

	ReportMouse(TRUE , win);

	SetMenuStrip(win , &Menu);

	while (msg = GetMsg(win->UserPort) )
		ReplyMsg(msg);


	really_open_window = 1;

loop:
	WaitPort(win->UserPort);

	msg = GetMsg(win->UserPort);

	class = msg->Class;
	code = msg->Code;
	x = msg->MouseX;
	y = msg->MouseY;

	ReplyMsg(msg);

	Forbid();

	if (y < 10)
		win->Flags &= ~RMBTRAP;
	else
		win->Flags |= RMBTRAP;

	Permit();

	if (class == MENUPICK && code != MENUNULL)
		bye();

	else if (class == MOUSEBUTTONS)
	{
		if (code == SELECTDOWN)
		{
			ry = ( (fy = y) - conUnit->cu_YROrigin) / 8;
			pressed = 1;
		}

		else if (code == SELECTUP)
		{
			prx = -1111;
			px = -1111;
			maxx = -1111;

			pressed = 0;
		}

		else if (code == MENUDOWN)
		{
			rx = (x - conUnit->cu_XROrigin) / 8;
			ry = (y - conUnit->cu_YROrigin) / 8;

			ie.ie_Qualifier = NULL;

			if (ry > conUnit->cu_YCCP)
			{
				ie.ie_Code = 0x44;

				DoIO(req);

				ie.ie_Code = 0x44 | 0x80;

				DoIO(req);
			}

			else if (ry == conUnit->cu_YCCP)
			{
				if (rx > conUnit->cu_XCCP)
				{
					for (i = rx - conUnit->cu_XCCP + 1 ; i ; i--)
					{
						ie.ie_Code = 0x46;

						DoIO(req);

						ie.ie_Code = 0x46 | 0x80;

						DoIO(req);
					}
				}

				else if (rx < conUnit->cu_XCCP)
				{
					for (i = conUnit->cu_XCCP - rx ; i ; i--)
					{
						ie.ie_Code = 0x41;

						DoIO(req);

						ie.ie_Code = 0x41 | 0x80;

						DoIO(req);
					}
				}

				else if (rx == conUnit->cu_XCCP)
					do_args(argc , argv);
			}

			prx = -1111;
			px = -1111;
			maxx = -1111;

			pressed = 0;
		}
	}

	if (pressed && x >= conUnit->cu_XROrigin && y >= conUnit->cu_YROrigin && x <
				conUnit->cu_XRExtant && y < conUnit->cu_YRExtant)
	{
		rx = (x - conUnit->cu_XROrigin) / 8;

		if (rx != prx && rx > maxx)
		{
			maxx = rx;

			if (rx > prx && rx - prx > 1 && prx != -1111)
			{
				for (i = rx - prx + 1 , x = px + 8 ; i && x <
						conUnit->cu_XRExtant ; x += 8 , i--)
					convert_char(x , fy);
				
				x -= 8;
			}

			else
				convert_char(x , fy);

			rx = (x - conUnit->cu_XROrigin) / 8;

			if (rx > maxx)
				maxx = rx;

			prx = rx;

			px = x;
		}
	}

	goto loop;

	bye();
}

bye()
{
	register int i;

	for (i = 0 ; i != num_of_chrs ; i++)
		if (chrs[i].pln)
			FreeRaster(chrs[i].pln , 8L , 8L);

	if (tbm->Planes[0])
		FreeRaster(tbm->Planes[0] , 8L , 8L);

	if (conWindow && really_open_window)
	{
		Forbid();

		conWindow->Flags &= ~RMBTRAP;

		Permit();

		ClearMenuStrip(conWindow);

		ReportMouse(FALSE , conWindow);

		ModifyIDCMP(conWindow , NULL);
	}

	if (IntuitionBase)
		CloseLibrary(IntuitionBase);

	if (GfxBase)
		CloseLibrary(GfxBase);

	if (NOT errdev && req)
		CloseDevice(req);

	if (req)
		DeleteStdIO(req);

	if (port)
		DeletePort(port);

	exit(0);
}

extern long sendpkt();

LONG findWindow() /* inits conWindow and conUnit (global vars) */
   {
   register struct InfoData *id;
	register    struct MsgPort  *conid;
	register    struct Process  *me;
	register   LONG myargs[8] ,nargs, res1;

   /* Alloc to insure longword alignment */
   id = (struct InfoData *)AllocMem( (long) sizeof(struct InfoData),
                                       MEMF_PUBLIC|MEMF_CLEAR);
   if(! id) return(0);
   me = (struct Process *) FindTask(NULL);
   conid = (struct MsgPort *) me->pr_ConsoleTask;

   myargs[0]=((ULONG)id) >> 2;
   nargs = 1;
   res1 = (LONG)sendpkt(conid,ACTION_DISK_INFO,myargs,nargs);
   conWindow = (struct Window *)id->id_VolumeNode;
   conUnit = (struct ConUnit *)
                 ((struct IOStdReq *)id->id_InUse)->io_Unit;
   FreeMem(id, (long) sizeof(struct InfoData));
   return(res1);
}


LONG sendpkt(pid,action,args,nargs)
register struct MsgPort *pid;  /* process indentifier ... (handlers message port ) */
register LONG action,          /* packet type ... (what you want handler to do )   */
     args[],          /* a pointer to a argument list */
     nargs;           /* number of arguments in list  */
   {
	register    struct MsgPort        *replyport;
   struct StandardPacket *packet;
 
	register    LONG  count, *pargs, res1;

   replyport = (struct MsgPort *) CreatePort( (long) NULL, (long) 0);
   if(!replyport) return( (long) NULL);

   packet = (struct StandardPacket *) 
      AllocMem((long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR);
   if(!packet) 
      {
      DeletePort(replyport);
      return(NULL);
      }

   packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
   packet->sp_Pkt.dp_Link         = &(packet->sp_Msg);
   packet->sp_Pkt.dp_Port         = replyport;
   packet->sp_Pkt.dp_Type         = action;

   /* copy the args into the packet */
   pargs = &(packet->sp_Pkt.dp_Arg1);       /* address of first argument */
   for(count=0;count < nargs;count++) 
      pargs[count]=args[count];
 
   PutMsg(pid,packet); /* send packet */

   WaitPort(replyport);
   GetMsg(replyport); 

   res1 = packet->sp_Pkt.dp_Res1;

   FreeMem(packet,(long)sizeof(struct StandardPacket));
   DeletePort(replyport); 

   return(res1);
}
