
/*
 *  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.
 *		Huh? Clearing FIFOF_EOF is commented out in fifo.c so
 *		that EOF is reported every time.  See TODO (Jörg Höhle)
 */

#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/conunit.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/fifo.h>
#if defined(_DCC) /*  at least the very old DICE 2.06 */
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#else
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#endif
#include <clib/alib_protos.h>
#include <proto/fifo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>		/*  write() */
#if defined(__GNUC__)
#include <unistd.h>		/*  write() */
#endif
#ifdef USE_SIGNAL
#include <signal.h>
#endif

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;
typedef struct IORequest    IORequest;

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

char	IocrIP; 	/*  console read pending*/
char	IocrHold;
char	FifrIP; 	/*  read msg pending	*/
char	FifwIP; 	/*  write msg pending	*/
short	Done = 0;

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
};

const char verstring[] = "\0$VER: RemCLI 38.1 (20.12.95)\r\n";

#define errline(s,len) write(2, s, len)
#define errmsg(s) errline(s, sizeof(s)-1)
#define errmsg1(s,v) {int errlen = sprintf(ErrBuf, s, v); errline(ErrBuf, errlen);}

void ConWriteStr(char *);
void ConWrite(void *, long);
void SendBreak(int);
void myexit(void);
void WaitMsg(Message *);
#ifndef USE_SIGNAL
#ifdef _DCC
int  brk(void);
#endif
#if defined(__GNUC__)		/*  actually libnix only */
void __chkabort(void);
#endif
#endif

int main(int ac, char **av)
{
    long imask;
    long pmask;

    if (ac != 2) {
	errmsg("Demonstration Remote Shell through FIFO:\n");
	errmsg("Instructions:\n");
	errmsg("  1> NewShell FIFO:name/rwkecs\n");
	errmsg("  1> Run >NIL: RemCLI name\n");
	exit(1);
    }
    atexit(myexit);
#ifndef USE_SIGNAL
#ifdef _DCC
    onbreak(brk);		/*  onbreak() generates shorter code than signal() */
#endif
#else
    signal(SIGINT, SIG_IGN);
#endif

    FifoName = av[1];
    {
	int len  = strlen(FifoName);

	FifoSlav = malloc(len + (len<<1) + (3 + 3 + 35));
	FifoMast = FifoSlav + len + 3;
	ErrBuf   = FifoMast + len + 3;
    }
    sprintf(FifoMast, "%s_m", FifoName);
    sprintf(FifoSlav, "%s_s", FifoName);

    IoSink = CreatePort(NULL, 0);

    /*
     *	FIFOS
     */

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

    /*
     *	WINDOW
     */

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

    Win = OpenWindow(&Nw);
    if (Win == NULL) {
	errmsg("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, (IORequest *)&Iocw, 0)) {
	errmsg("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((IORequest *)&Iocr);
    IocrIP = 1;

    /*
     *	start async FIFO requests
     */

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

    /*
     * start shell for slave side
     */

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

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

	    while ((im = (IMess *)GetMsg(Win->UserPort))) {
		switch(im->Class) {
		case CLOSEWINDOW:
		    Done = 2;
		    mask = 0;
		    break;
		case NEWSIZE:
		    ConWriteStr("\017\233\164\233\165\233\166\233\167");
		    break;
		}
		ReplyMsg((Message *)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((IORequest *)&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");
			Done = 1;
		    } 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(void)
{
    if (IocrIP) {
	AbortIO((IORequest *)&Iocr);
	WaitIO((IORequest *)&Iocr);
    }
    if (Iocr.io_Device)
	CloseDevice((IORequest *)&Iocr);

    if (FifrIP) {
	RequestFifo(FifoR, &RMsg, FREQ_ABORT);
	WaitMsg(&RMsg);
    }
    if (FifwIP) {
	RequestFifo(FifoW, &WMsg, FREQ_ABORT);
	WaitMsg(&WMsg);
    }
    if (FifoR)
	CloseFifo(FifoR, FIFOF_EOF);
    if (FifoW)
	/*  no FIFOF_EOF on CLOSEWINDOW to conform to documentation */
	CloseFifo(FifoW, Done == 2 ? 0 : FIFOF_EOF);
    if (FifoBase)
	CloseLibrary(FifoBase);
    if (IntuitionBase)
	CloseLibrary((struct Library *)IntuitionBase);

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

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

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

#ifndef USE_SIGNAL
#ifdef _DCC
int brk(void)
{
    return(0);			/*  ignore ^C */
}
#endif
#if defined(__GNUC__)		/*  libnix internal function */
void __chkabort(void)
{
}
#endif
#endif

void WaitMsg(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(int c)
{
    char buf[256];
    long fh;

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

