
/*
 *  REMCLI.C
 *
 *  REMCLI <fifo_name>
 *
 *  PUBLIC DOMAIN CODE, sample code showing how to set up a remote CLI
 *  through the FIFO: device, using fifo.library to handle the master
 *  end.
 *
 *  WARNING:	ReadFifo() returns an EOF condition (-1) only once,
 *		calling it again will return 0 .. blocking until a
 *		writer writes something then closes again to generate
 *		another EOF.
 */

#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <devices/conunit.h>
#include <hardware/cia.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <stdio.h>
#include <stdlib.h>
#include "fifo.h"

typedef unsigned char	ubyte;
typedef unsigned short	uword;
typedef unsigned long	ulong;
typedef struct MsgPort	    MsgPort;

typedef struct Task	    Task;
typedef struct Process	    Process;
typedef struct Window	    Window;
typedef struct IOStdReq     Ioc;
typedef struct Message	    Message;
typedef struct IntuiMessage IMess;

extern void *CreatePort();
extern void *AllocMem();
extern void *OpenFifo();

Ioc	Iocr;
Ioc	Iocw;
Message RMsg;
Message WMsg;
Window	*Win;
char	*FifoName;	/*  fifo    */
char	*FifoSlav;	/*  fifo_s  */
char	*FifoMast;	/*  fifo_m  */
MsgPort *IoSink;
char	ConBuf[32];
void	*FifoR;
void	*FifoW;
long	FifoBase;
long	IntuitionBase;

char	IocrIP; 	/*  console read pending*/
char	IocrHold;
char	FifrIP; 	/*  read msg pending	*/
char	FifwIP; 	/*  write msg pending	*/

struct NewWindow Nw = {
    0, 0, 640, 200, -1, -1,
    CLOSEWINDOW|MENUPICK|GADGETUP|GADGETDOWN|NEWSIZE,
    WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SMART_REFRESH|
    NOCAREREFRESH|ACTIVATE,
    NULL, NULL, (ubyte *)"RemCLI", NULL, NULL, 32, 32, -1, -1, WBENCHSCREEN
};

extern Window *OpenWindow();

void ConWriteStr(char *);
void ConWrite(void *, long);
void SendBreak(int);
void myexit();
void WaitMsg(Message *);
int  brk();

main(ac, av)
char **av;
{
    long imask;
    long pmask;
    short notDone = 1;

    if (ac == 1) {
	fprintf(stderr, "Demonstration Remote Shell through FIFO:\n");
	fprintf(stderr, "Instructions:\n");
	fprintf(stderr, "  1> NewShell FIFO:name/rwkecs\n");
	fprintf(stderr, "  1> RemCLI name\n");
	exit(1);
    }
    atexit(myexit);
    onbreak(brk);

    FifoName = av[1];
    FifoSlav = malloc(strlen(FifoName) + 16);
    FifoMast = malloc(strlen(FifoName) + 16);
    sprintf(FifoMast, "%s_m", FifoName);
    sprintf(FifoSlav, "%s_s", FifoName);

    IoSink = CreatePort(NULL, 0);

    /*
     *	FIFOS
     */

    FifoBase = OpenLibrary(FIFONAME, 0);
    if (!FifoBase) {
	fprintf(stderr, "unable to open %s\n", FIFONAME);
	exit(1);
    }
    FifoW = OpenFifo(FifoMast, 2048, FIFOF_WRITE | FIFOF_NORMAL | FIFOF_NBIO);
    if (FifoW == NULL) {
	fprintf(stderr, "unable to open fifo %s\n", FifoMast);
	exit(1);
    }
    FifoR = OpenFifo(FifoSlav, 2048, FIFOF_READ  | FIFOF_NORMAL | FIFOF_NBIO);
    if (FifoR == NULL) {
	fprintf(stderr, "unable to open fifo %s\n", FifoSlav);
	exit(1);
    }
    RMsg.mn_ReplyPort = IoSink;
    WMsg.mn_ReplyPort = IoSink;

    /*
     *	WINDOW
     */

    IntuitionBase = OpenLibrary("intuition.library", 0);
    if (!IntuitionBase) {
	fprintf(stderr, "unable to open intuition.library\n");
	exit(1);
    }

    Win = OpenWindow(&Nw);
    if (Win == NULL) {
	fprintf(stderr, "unable to open window!\n");
	exit(1);
    }
    imask = 1 << Win->UserPort->mp_SigBit;
    pmask = 1 << IoSink->mp_SigBit;

    /*
     *	CONSOLE DEVICE
     */

    Iocw.io_Message.mn_ReplyPort = IoSink;
    Iocw.io_Data = (APTR)Win;
    Iocw.io_Length = sizeof(*Win);
    if (OpenDevice("console.device", 0, &Iocw, 0)) {
	fprintf(stderr, "can't open console!\n");
	exit(1);
    }
    Iocr = Iocw;
    Iocw.io_Command = CMD_WRITE;
    Iocr.io_Command = CMD_READ;

    Iocr.io_Data = (APTR)ConBuf;
    Iocr.io_Length = 1;
    SendIO(&Iocr);
    IocrIP = 1;

    /*
     *	start async FIFO requests
     */

    RequestFifo(FifoR, &RMsg, FREQ_RPEND);
    FifrIP = 1;

    /*
     * start shell for slave side
     */

    while (notDone) {
	long mask = Wait(imask | pmask);

top:
	if (mask & imask) {
	    IMess *im;

	    while (im = (IMess *)GetMsg(Win->UserPort)) {
		switch(im->Class) {
		case CLOSEWINDOW:
		    notDone = 0;
		    mask = 0;
		    break;
		case NEWSIZE:
		    ConWriteStr("\017\233\164\233\165\233\166\233\167");
		    break;
		}
		ReplyMsg(im);
	    }
	}
	if (mask & pmask) {
	    Message *msg;
	    int n;

	    while (msg = (Message *)GetMsg(IoSink)) {
		if (msg == (Message *)&Iocr) {
		    IocrIP = 0;
		    if (Iocr.io_Actual > 0) {
			switch(*(char *)Iocr.io_Data) {
			case 3:
			    SendBreak('C');
			    break;
			case 4:
			    SendBreak('D');
			    break;
			case 5:
			    SendBreak('E');
			    break;
			case 6:
			    SendBreak('F');
			    break;
			default:
			    n = WriteFifo(FifoW, Iocr.io_Data, Iocr.io_Actual);
			    if (n != Iocr.io_Actual) {
				IocrHold = 1;
				if (FifwIP == 0) {
				    RequestFifo(FifoW, &WMsg, FREQ_WAVAIL);
				    FifwIP = 1;
				}
			    }
			    break;
			}
		    }
		    if (IocrHold == 0) {
			Iocr.io_Data = (APTR)ConBuf;
			Iocr.io_Length = 1;
			SendIO(&Iocr);
			IocrIP = 1;
		    }
		} else
		if (msg == (Message *)&RMsg) {
		    char *ptr;
		    long n;

		    FifrIP = 0;

		    if ((n = ReadFifo(FifoR, &ptr, 0)) > 0) {
			if (n > 256)                /*  limit size      */
			    n = 256;
			ConWrite(ptr, n);
						    /*	clear N bytes	*/
			n = ReadFifo(FifoR, &ptr, n);
		    }
		    if (n < 0) {            /*  EOF */
			ConWriteStr("REMOTE EOF!\n");
			notDone = 0;
		    } else {
			RequestFifo(FifoR, &RMsg, FREQ_RPEND);
			FifrIP = 1;
		    }
		} else
		if (msg == (Message *)&WMsg) {
		    FifwIP = 0;
		    if (IocrHold) {                 /*  retry rd process */
			ReplyMsg(&Iocr.io_Message);
			IocrHold = 0;
		    }
		}
		goto top;
	    }
	}
    }
}

void
myexit()
{
    if (IocrIP) {
	AbortIO(&Iocr);
	WaitIO(&Iocr);
    }
    if (Iocr.io_Device)
	CloseDevice(&Iocr);

    if (FifrIP) {
	RequestFifo(FifoR, &RMsg, FREQ_ABORT);
	WaitMsg(&RMsg);
    }
    if (FifwIP) {
	RequestFifo(FifoW, &WMsg, FREQ_ABORT);
	WaitMsg(&RMsg);
    }
    if (FifoR)
	CloseFifo(FifoR);
    if (FifoW)
	CloseFifo(FifoW);
    if (FifoBase)
	CloseLibrary(FifoBase);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);

    if (Win)
	CloseWindow(Win);
    if (IoSink)
	DeletePort(IoSink);
}

void
ConWriteStr(buf)
char *buf;
{
    ConWrite(buf, strlen(buf));
}

void
ConWrite(buf, len)
void *buf;
{
    Iocw.io_Data = (APTR)buf;
    Iocw.io_Length = len;
    DoIO(&Iocw);
}

int
brk()
{
    return(0);
}

void
WaitMsg(msg)
Message *msg;
{
    while (msg->mn_Node.ln_Type == NT_MESSAGE)
	Wait(1 << msg->mn_ReplyPort->mp_SigBit);
    Forbid();
    Remove(&msg->mn_Node);
    Permit();
}

void
SendBreak(c)
int c;
{
    char buf[256];
    long fh;

    sprintf(buf, "FIFO:%s/%c", FifoName, c);
    if (fh = Open(buf, 1005))
	Close(fh);
}
