/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: Print-Handler.c
 *	Created ..: Wednesday 11-Apr-90 09:51
 *	Revision .: 6
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	11-Apr-90       Olsen           Created this file!
 *
 ****************************************************************************
 *
 *	Notez-bien: This version of Print-Handler will only compile under
 *                  Aztec 'C' 5.0. Some effort must be taken to adapt it
 *                  for Lattice 'C' 5.0.
 *
 *	I've left all my debugs in and commented them out. This should
 *	compile fine now.
 *
 * $Revision Header ********************************************************/

	/* Function prototypes (no includes this time). */

struct DataSeg *	CreateSeg(APTR,LONG);
struct DataSeg *	DeleteSeg(struct DataSeg *);
VOID			ReturnPacket(struct DosPacket *,ULONG,ULONG);
struct DosPacket *	TaskWait(VOID);
LONG			PrintIt(APTR,LONG);
LONG			PrintData(UBYTE *,LONG,struct PopSupport *);
VOID *			SendCustomMsg(LONG);

VOID			QueueEntryPoint(VOID);
LONG			_main(VOID);

	/* This structure is used to hold a fragment of the file to
	 * be printed.
	 */

struct DataSeg
{
	struct DataSeg	*NextSeg;

	APTR		 Buffer;
	LONG		 Length;
};

	/* Shared library structures. */

struct IntuitionBase	*IntuitionBase;
struct MXMBase		*MXMBase;
/*struct Library		*FarBase;*/
extern struct ExecBase	*SysBase;

	/* This is the maximum length of a printer line. */

#define MAXPRINT	1024

	/* This is the maximum number of available slots. */

#define MAXSLOT		128

	/* Handshake signal. */

#define SIG_SHAKE	SIGBREAKF_CTRL_D

	/* Some more global data. */

struct Preferences	*Preferences;
struct IOStdReq		*PrinterDevice;
struct Process		*QueueProc;
struct Process		*HandlerProc;
struct DataSeg		*DataSegs[MAXSLOT];
UBYTE			 Available[MAXSLOT];
UBYTE			 LineStack[MAXPRINT];

	/* Working stats for the printer process. */

LONG LinesDone = 0,ColumnsDone = 0;

	/* _main():
	 *
	 *	This is the main entry point to the handler
	 *	process.
	 */

LONG
_main()
{
	struct DosPacket	*PrintPacket;
	struct DeviceNode	*PrintNode;
	LONG			 OpenCount = 0,i;

		/* Mark all buffers as vacant. */

	memset(Available,TRUE,MAXSLOT);

	HandlerProc = (struct Process *)SysBase -> ThisTask;

		/* Started from CLI? */

	if(HandlerProc -> pr_CLI)
		return(-1);

		/* Wait for parameter packet. */

	PrintPacket = TaskWait();

		/* Pointer to our own DeviceNode. */

	PrintNode = (struct DeviceNode *)BADDR(PrintPacket -> dp_Arg3);

		/* Open mxm.library. */

	if(!(MXMBase = (struct MXMBase *)OpenLibrary("mxm.library",34)))
	{
		ReturnPacket(PrintPacket,DOSFALSE,PrintPacket -> dp_Res2);
		goto FallOff;
	}

/*	if(!(FarBase = OpenLibrary("farprint.library",0)))*/
/*	{*/
/*		ReturnPacket(PrintPacket,DOSFALSE,PrintPacket -> dp_Res2);*/
/*		goto FallOff;*/
/*	}*/

		/* Extract global library vector. */

	IntuitionBase = (struct IntuitionBase *)MXMBase -> IntuitionBase;

		/* Create the printer process. */

	if(!(QueueProc = CreateFuncProc("Print-Handler.queue",10,QueueEntryPoint,4096)))
	{
		ReturnPacket(PrintPacket,DOSFALSE,PrintPacket -> dp_Res2);
		goto FallOff;
	}

		/* Wait for a reply. */

	Wait(SIG_SHAKE);

		/* Process quietly removed itself. */

	if(!QueueProc)
	{
		ReturnPacket(PrintPacket,DOSFALSE,PrintPacket -> dp_Res2);
		goto FallOff;
	}

		/* We're on the scene now. */

	PrintNode -> dn_Task = &HandlerProc -> pr_MsgPort;

		/* Return the compliment. */

	ReturnPacket(PrintPacket,DOSTRUE,PrintPacket -> dp_Res2);

		/* Go into loop. */

	for(;;)
	{
			/* Wait for the packet. */

/*		SendText("TaskWait");*/

		PrintPacket = TaskWait();

/*		SendText("Type = %ld",PrintPacket -> dp_Type);*/

		switch(PrintPacket -> dp_Type)
		{
				/* Somebody Open()ed us. */

			case ACTION_FINDINPUT:
			case ACTION_FINDOUTPUT:
			case ACTION_FINDUPDATE:
			{
				struct FileHandle *FileHandle = (struct FileHandle *)BADDR(PrintPacket -> dp_Arg1);

/*				SendText("Open");*/

					/* Assume failure. */

				FileHandle -> fh_Port = DOSFALSE;

					/* Look for an empty buffer. */

				for(i = 0 ; i < MAXSLOT ; i++)
				{
					if(Available[i])
					{
						FileHandle -> fh_Port = (struct MsgPort *)DOSTRUE;
						FileHandle -> fh_Arg1 = i;

						Available[i] = FALSE;

/*						SendText("Clean open! -> %ld.",i);*/

						OpenCount++;

						break;
					}
				}

				ReturnPacket(PrintPacket,(LONG)FileHandle -> fh_Port,PrintPacket -> dp_Res2);
				break;
			}

				/* A FileHandle got closed. */

			case ACTION_END:
			{
				OpenCount--;

/*				SendText("Close -> %ld",PrintPacket -> dp_Arg1);*/

				SendCustomMsg(PrintPacket -> dp_Arg1);

				ReturnPacket(PrintPacket,DOSTRUE,PrintPacket -> dp_Res2);

				break;
			}

				/* We always read *nothing*. */

			case ACTION_READ:
			{
/*				SendText("Read -> %ld",PrintPacket -> dp_Arg1);*/

				ReturnPacket(PrintPacket,NULL,PrintPacket -> dp_Res2);
				break;
			}

				/* Something gets written to a FileHandle. */

			case ACTION_WRITE:
			{
				char *Buffer = (char *)PrintPacket -> dp_Arg2;
				struct DataSeg *NextSlot;

/*				SendText("Write -> %ld Bytes %ld Buffer %lx",PrintPacket -> dp_Arg1,PrintPacket -> dp_Arg3,Buffer);*/

				PrintPacket -> dp_Res1 = PrintPacket -> dp_Arg3;

					/* Add the new data. */

				if(!DataSegs[PrintPacket -> dp_Arg1])
				{
					if(!(DataSegs[PrintPacket -> dp_Arg1] = CreateSeg((APTR)Buffer,PrintPacket -> dp_Arg3)))
					{
						PrintPacket -> dp_Res2 = ERROR_NO_FREE_STORE;
						PrintPacket -> dp_Res1 = 0;
					}
/*					else*/
/*						SendText("Created.");*/
				}
				else
				{
					NextSlot = DataSegs[PrintPacket -> dp_Arg1];

					while(NextSlot -> NextSeg)
						NextSlot = NextSlot -> NextSeg;

					if(!(NextSlot -> NextSeg = CreateSeg((APTR)Buffer,PrintPacket -> dp_Arg3)))
					{
						PrintPacket -> dp_Res2 = ERROR_NO_FREE_STORE;
						PrintPacket -> dp_Res1 = 0;
					}
/*					else*/
/*						SendText("Appended.");*/
				}

				ReturnPacket(PrintPacket,PrintPacket -> dp_Res1,PrintPacket -> dp_Res2);
				break;
			}

				/* We are to leave the town. */

			case ACTION_DIE:
			{
				ReturnPacket(PrintPacket,DOSTRUE,0);

				goto FallOff;
			}

				/* Say what? */

			default:
			{
				ReturnPacket(PrintPacket,DOSFALSE,ERROR_ACTION_NOT_KNOWN);

				break;
			}
		}
	}

FallOff:PrintNode -> dn_Task = NULL;

/*	SendText("FallOff");*/

		/* Tell printer process to shut down. */

	if(QueueProc)
	{
		SendCustomMsg(-1);

/*		SendText("Handshake...");*/

		Wait(SIG_SHAKE);
	}

/*	SendText("Close mxm");*/

		/* Close the library. */

	if(MXMBase)
		CloseLibrary((struct Library *)MXMBase);

/*	SendText("Close far");*/

/*	if(FarBase)*/
/*		CloseLibrary(FarBase);*/
}

	/* SendCustomMsg(Slot):
	 *
	 *	Send a message to the printer process.
	 */

VOID *
SendCustomMsg(Slot)
LONG Slot;
{
	struct Message *TempMsg = (struct Message *)AllocRem(sizeof(struct Message),MEMF_PUBLIC | MEMF_CLEAR);

	if(TempMsg)
	{
		TempMsg -> mn_Node . ln_Name	= (char *)Slot;
		TempMsg -> mn_ReplyPort		= NULL;
		TempMsg -> mn_Length		= sizeof(struct Message);

/*		SendText("SendCustomMsg -> %ld",Slot);*/

		PutMsg(&QueueProc -> pr_MsgPort,TempMsg);
	}

	return((VOID *)TempMsg);
}

	/* QueueEntryPoint():
	 *
	 *	The printer process. This revision now send the data
	 *	to be printed to the process rather than to process
	 *	the data itself. This saves some amount of time when
	 *	calling processes send their files while the printer
	 *	is still busy.
	 */

VOID
QueueEntryPoint()
{
	struct MsgPort		*PrinterPort;
	struct Message		*QueueMsg;
	LONG			 GoOn,Slot,i;
	struct PopSupport	 PopSupport;

	geta4();

		/* We want the error pop-ups to beep. */

	PopSupport . ps_Flags	= PS_BEEP;
	PopSupport . ps_TimeOut	= 0;

		/* Open everything this process needs. */

	if(!(PrinterPort = (struct MsgPort *)CreatePort(NULL,0)))
		goto FallOff;

	if(!(PrinterDevice = (struct IOStdReq *)CreateStdIO(PrinterPort)))
		goto FallOff;

	if(OpenDevice("printer.device",0,PrinterDevice,0))
		goto FallOff;

	Preferences = &((struct PrinterData *)PrinterDevice -> io_Device) -> pd_Preferences;

		/* We're running. */

	Signal((struct Task *)HandlerProc,SIG_SHAKE);

/*	SendText("Process running.");*/

		/* Go into infinite loop. */

	FOREVER
	{
		WaitPort(&QueueProc -> pr_MsgPort);

		while(QueueMsg = GetMsg(&QueueProc -> pr_MsgPort))
		{
			GoOn = TRUE;

			if((Slot = (LONG)QueueMsg -> mn_Node . ln_Name) != -1)
			{
/*				SendText("Slot %ld",Slot);*/

				while(DataSegs[Slot])
				{
					if(GoOn)
					{
						if(!PrintData((UBYTE *)DataSegs[Slot] -> Buffer,DataSegs[Slot] -> Length,&PopSupport))
							GoOn = FALSE;
					}

					DataSegs[Slot] = DeleteSeg(DataSegs[Slot]);
				}

				/* New: produce a working formfeed if last page
				 * wasn't 100% occupied.
				 */

				if(Preferences -> PaperType == SINGLE && LinesDone < Preferences -> PaperLength)
				{
					LONG ToDo = Preferences -> PaperLength - LinesDone;

					for(i = 0 ; i < ToDo ; i++)
						PrintIt((APTR)"\n",1);
				}

				GoOn = TRUE;

				Available[Slot] = TRUE;
			}
			else
				GoOn = FALSE;

			FreeRem(QueueMsg);

			LinesDone = ColumnsDone = 0;

			if(!GoOn)
				goto FallOff;
		}
	}

FallOff:if(PrinterDevice)
	{
/*		SendText("CloseDevice");*/

		if(PrinterDevice -> io_Device)
			CloseDevice(PrinterDevice);

/*		SendText("DeleteStdIO");*/

		DeleteStdIO(PrinterDevice);
	}

/*	SendText("DeletePort");*/

	if(PrinterPort)
		DeletePort(PrinterPort);

	Forbid();

	Signal((struct Task *)HandlerProc,SIG_SHAKE);

	QueueProc = NULL;
}

	/* PrintIt(Buffer,Length):
	 *
	 *	The real printer interface.
	 */

LONG
PrintIt(Buffer,Length)
APTR Buffer;
LONG Length;
{
	if(Buffer && Length)
	{
		LONG Error;

		PrinterDevice -> io_Command	= CMD_WRITE;
		PrinterDevice -> io_Data	= Buffer;
		PrinterDevice -> io_Length	= Length;

		Error = DoIO(PrinterDevice); 

		return(Error);
	}

	return(IOERR_BADLENGTH);
}

	/* PrintData(Buffer,Length,PopSupport):
	 *
	 *	This one send the data to the printer.
	 */

LONG
PrintData(Buffer,Length,PopSupport)
UBYTE *Buffer;
LONG Length;
struct PopSupport *PopSupport;
{
	LONG i,InBuff = 0,PaperWidth = Preferences -> PrintRightMargin - Preferences -> PrintLeftMargin;

/*	SendText("Buffer %lx Length %ld",Buffer,Length);*/

	memset(LineStack,0,MAXPRINT);

	if(Buffer)
	{
		for(i = 0 ; i < Length ; i++)
		{
			LineStack[InBuff++] = Buffer[i];
			ColumnsDone++;

			if(ColumnsDone == PaperWidth || Buffer[i] == '\n')
			{
				if(ColumnsDone == PaperWidth || ColumnsDone == MAXPRINT)
				{
					LineStack[InBuff++] = '\n';
					ColumnsDone++;
				}

				LineStack[InBuff] = 0;

				if(PrintIt((APTR)LineStack,InBuff))
					PopRequest(NULL,"Print-Handler Problem:","Error copying text to printer,\nplease    check   cables   and\nsupplies.",NULL,"Continue?",FALSE,PopSupport);

				if((++LinesDone) == Preferences -> PaperLength)
				{
					LinesDone = 0;

					if(Preferences -> PaperType == SINGLE)
					{
						if(!PopRequest(NULL,"Print-Handler Request:","Finished   with   current  page,\nplease   insert  next  sheet  of\npaper.","Continue","Abort",TRUE,NULL))
							return(FALSE);
					}
				}

				ColumnsDone = InBuff = 0;

				memset(LineStack,0,MAXPRINT);
			}
		}

		if(InBuff)
			PrintIt((APTR)LineStack,InBuff);
	}

	return(TRUE);
}

	/* CreateSeg(Buffer,Length):
	 *
	 *	Create a new data segment.
	 */

struct DataSeg *
CreateSeg(Buffer,Length)
APTR Buffer;
LONG Length;
{
	struct DataSeg *NewSeg;

/*	SendText("CreateSeg Length %ld",Length);*/

	if(NewSeg = (struct DataSeg *)AllocRem(sizeof(struct DataSeg),MEMF_PUBLIC | MEMF_CLEAR))
	{
		if(NewSeg -> Buffer = (APTR)AllocRem(Length,MEMF_PUBLIC | MEMF_CLEAR))
		{
			CopyMem(Buffer,NewSeg -> Buffer,Length);
			NewSeg -> Length = Length;

/*			SendText("Buffer %lx",NewSeg -> Buffer);*/

			return(NewSeg);
		}

		FreeRem(NewSeg);
	}

	return(NULL);
}

	/* DeleteSeg(OldSeg):
	 *
	 *	Delete a data segment.
	 */

struct DataSeg *
DeleteSeg(OldSeg)
struct DataSeg *OldSeg;
{
	struct DataSeg *NextSeg = NULL;

	if(OldSeg)
	{
		NextSeg = OldSeg -> NextSeg;

		if(OldSeg -> Buffer && OldSeg -> Length)
		{
			FreeRem(OldSeg -> Buffer);

			OldSeg -> Buffer = NULL;
			OldSeg -> Length = 0;
		}

		FreeRem(OldSeg);
	}

	return(NextSeg);
}

	/* ReturnPacket(Packet,res1,res2):
	 *
	 *	Returns a packet to a handler.
	 */

VOID
ReturnPacket(Packet,Result1,Result2)
struct DosPacket *Packet;
ULONG Result1,Result2;
{
	struct MsgPort *ReplyPort = Packet -> dp_Port;

		/* Fill in the results and fill in the
		 * sender MsgPort.
		 */

	Packet -> dp_Res1 = Result1;
	Packet -> dp_Res2 = Result2; 
	Packet -> dp_Port = &HandlerProc -> pr_MsgPort;

		/* Link Message & Packet together. */

	Packet -> dp_Link -> mn_Node . ln_Name = (char *)Packet;
	Packet -> dp_Link -> mn_Node . ln_Succ = NULL;
	Packet -> dp_Link -> mn_Node . ln_Pred = NULL;

		/* Send the packet... */ 

	PutMsg(ReplyPort,Packet -> dp_Link); 
}

	/* TaskWait():
	 *
	 *	Lets the process wait for a DOS-Packet.
	 */

struct DosPacket *
TaskWait()
{
	struct Message *PrintMessage;

		/* Wait for the packet. */

	WaitPort(&HandlerProc -> pr_MsgPort);

		/* Pick it up. */

	PrintMessage = (struct Message *)GetMsg(&HandlerProc -> pr_MsgPort);

		/* Return real pointer. */

	return((struct DosPacket *)PrintMessage -> mn_Node . ln_Name);
} 
