/*
          Mr - Pager for piped files.

          Original effort by Fabio Rossetti.

          (c) 1989 by Fabio Rossetti

          To compile under Lattice C v5.0x use:

		lc -O -v s
		blink lib:cres.o s.o to s lib lib:a.lib lib:lc.lib sd nd
*/

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/libraries.h>

#include <devices/console.h>
#include <devices/conunit.h>

#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/arpbase.h>


#include <arpfunctions.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>

#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>

#include <graphics/text.h>

#define BFSIZE 1024
#define LINSIZE 512

#define ENDFILE 0
#define FORMFEED -1
#define LINEOK 1

/* command line arguments */

/*        */
/* global */
/*        */

struct ArpBase *ArpBase;
struct IntuitionBase *IntuitionBase;
struct Window *CliWin; 	/* poUSHORTer to console window */
struct Process *Pr;


USHORT dsplin,x,y,xf,yf,count,bp=0;
BPTR stdi;
TEXT *Buf,*Lin;
TEXT *Morpos="\033[0;0H\033[000;2H\033[7m\033[3m  ";
#define CYPOS 8
#define CYOFF 48
struct Window *w;

/* this is exec stuff for GetWin, to be kept global for Cleanup() */
struct MsgPort iorp = {
    {0, 0, NT_MSGPORT, 0, 0}, 0,
    -1,				/* initialize signal to -1 */
    0,
				/* start with empty list */
    {&iorp.mp_MsgList.lh_Tail, 0, &iorp.mp_MsgList.lh_Head, 0, 0}
};
struct IOStdReq ior = {
    {{0, 0, 0, 0, 0}, &iorp, 0},
    0				/* device is zero */
};

VOID MemCleanup()
{
}

/* general shutdown routine*/
VOID Cleanup(code,retcode,msg)
LONG code;
LONG retcode;
STRPTR msg;
{
	if (ior.io_Device != 0) {
		if (iorp.mp_SigBit != -1) {
		FreeSignal(iorp.mp_SigBit);
		}
		CloseDevice(&ior);
	}

	CloseLibrary((struct Library*)ArpBase);

	if (msg) Puts(msg);

	Pr->pr_Result2=retcode;

	exit(code);
}
/* bulletproofly obtain a pointer to the CLI window sending a ACTION_DISK_INFO
   packet to the console process and looking into InfoData */
struct Window *GetWin(mode)
USHORT mode;
#define RAW -1
#define CON 0
#define POINTER 1

{
	struct MsgPort *con;
	struct StandardPacket *packet=NULL;
	struct InfoData *id=NULL;

	/* open the console device */
	if ((OpenDevice("console.device", -1, &ior, 0)) != 0) {
	Cleanup(RETURN_FAIL,ERROR_DEVICE_NOT_MOUNTED,NULL);
	}

	/* set up the message port in the I/O request */
	if ((iorp.mp_SigBit = AllocSignal(-1)) < 0) {
	Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"No mem");
	}
	iorp.mp_SigTask = (struct Task*)Pr;

	/* try to find console associated with calling process */
	/* if started from CLI, than is  */
	if ((iorp.mp_SigTask->tc_Node.ln_Type == NT_PROCESS)) {
	con = (struct MsgPort *)
		((struct Process *) iorp.mp_SigTask) -> pr_ConsoleTask;
	if (con != 0) {
		if ((packet = (struct StandardPacket *)
			ArpAlloc(sizeof(*packet)))) {

			/* this is the console handlers packet port */
			packet->sp_Msg.mn_Node.ln_Name = &(packet->sp_Pkt);
			packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
			packet->sp_Pkt.dp_Port = &iorp;
			if (mode == POINTER) {
			if (!(id = (struct id *) ArpAlloc(sizeof(*id))))
								return((struct Window *)-1);
			packet->sp_Pkt.dp_Type = ACTION_DISK_INFO;
			packet->sp_Pkt.dp_Arg1 = ((ULONG) id) >> 2;
					}
			else {
			packet->sp_Pkt.dp_Type = ACTION_SCREEN_MODE;
			packet->sp_Pkt.dp_Arg1 = mode;
			}
			PutMsg(con, packet);
		 	WaitPort(&iorp);
			/* Pointer to console window, all we need..*/
			if (mode == POINTER)
				return( (struct Window*)(id->id_VolumeNode));
			else return(0);
	}
	}
	/* error */
	return((struct Window *)-1);
}
}

VOID Clear(fl)
BPTR fl;
{
	(VOID)Write(fl,"\033[0;0H\033[J",9);
        dsplin = 0;
	x = (w->Width-24) / xf;
	y = (w->Height-16) / yf;
}

TEXT Banner(fl,ban)
BPTR fl;
STRPTR ban;
{

	TEXT ch;
	USHORT j = y+1;

	Morpos[CYPOS] = (TEXT)((j / 100) + CYOFF);
	Morpos[CYPOS+1] = (TEXT)(((j - ((j / 100)*100)) / 10) + CYOFF);
	Morpos[CYPOS+2] =
	(TEXT)((j - ((j / 100)*100) - ((j - ((j / 100)*100)) / 10)*10) + CYOFF);


	(VOID)Write(fl,Morpos,strlen(Morpos));
	(VOID)Write(fl,ban,strlen(ban));
	/* hide crsr */
	(VOID)Write(fl,"  \033[0m\033[K\033[43m \010",16);

        (VOID)Read(fl,&ch,1);
	(VOID)Write(fl,"\033[0m",4);

	return(ch);
}

Displine(fl)
BPTR fl;
{

	REGISTER TEXT chr;
	REGISTER ULONG ln = 0,actlin=0;

	for (;;) {

	if(!bp) if (!(count = Read(stdi,Buf,BFSIZE))) {
			return(ENDFILE);
			}

	chr = *(Buf + bp++);
	
	*(Lin + ln++) = chr;
	actlin++;
	switch (chr) {

		case '\n':
		Write(fl,Lin,ln);
		dsplin++;
		if (bp >= count) bp = 0;
		return(LINEOK);
		break;
		case '\t':
		actlin+=8;
		break;
		case '\010': /* backspace */
		actlin--;
		break;
		case '\014':
		Write(fl,Lin,ln-1);
		Write(fl,"\n^L",3);
		if (bp >= count) bp = 0;
		return(FORMFEED);
		break;
	}
	if (actlin >= x) {
		Write(fl,Lin,ln);
		dsplin++;
		if (bp >= count) bp = 0;
		return (LINEOK);
		}
	if (bp >= count) bp = 0;
	}
}

VOID Bye(fl)
BPTR fl;
{
 	Write(fl,"\015\033[K",4);
	(VOID)GetWin(CON);
	Cleanup(RETURN_OK,NULL,NULL);
}

/* _main used instead of main to slim code */
VOID _main(Line)
STRPTR Line;

{

	BPTR fil;
	ULONG st;
	TEXT c;

	Pr = (struct Process *) FindTask(NULL);
	if(!(ArpBase = (struct ArpBase*)OpenLibrary(ArpName,ArpVersion)))
		 Cleanup(RETURN_FAIL,ERROR_INVALID_RESIDENT_LIBRARY,NULL);


	if (!(Buf = ArpAllocMem(BFSIZE,MEMF_CLEAR)) ||

		!(Lin = ArpAllocMem(LINSIZE,MEMF_CLEAR)))
			Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"No mem");

	stdi = Input();

	if (IsInteractive(stdi)) Cleanup(RETURN_ERROR,NULL,"Use a pipe");

	w = GetWin(POINTER);

	xf = w->RPort->Font->tf_XSize;
	yf = w->RPort->Font->tf_YSize;

	(VOID)GetWin(RAW);

	fil = Open("*",MODE_OLDFILE);


	for (;;) {

	Clear(fil);
	while (dsplin <= (y-1)) {

		if ((st = Displine(fil)) != LINEOK) break;

			}

	if (st == ENDFILE) {
			Banner(fil,"--End of file--");
			break;
			}
	else {

	/* Goodbye Mr Wirth :-) */
	more: 
	 switch(c = Banner(fil,"--More--")) {

		case ' ':
		break;
		case 'q':
		Bye(fil);
		break;
		case '\003':
		Bye(fil);
		break;
		case '\015':
		Write(fil,"\015\033[K",4);
		Displine(fil);
		goto more;
		break;
		default:
		Banner(fil,"--???--");
	}
	}
}		
	Bye(fil);
}

