/* niftyterm.c -- nifty terminal emulator
 *
 * Copyright 1988, 1989 Chris Newman
 * All Rights Reserved
 * Permission is granted to copy, modify, and use this as long
 * as this notice remains intact.  This is a nifty program.
 *
 * Parts of this program were swiped from Joe Keane's jterm, other parts
 * were swiped from ITC's h19 terminal, and still others from gnu-emacs eterm.
 *
 * DISCLAIMER: the author (and maintainer) of this program is not responsible
 * for any damage or other problems caused by it.
 *
 * $Author: ppessi $ $Revision: 1.18 $ $Date: 1993/11/13 04:20:55 $
 */

static char rcsid[]=
 "$Id: niftyterm.c,v 1.18 1993/11/13 04:20:55 ppessi Exp ppessi $";

#include <stdio.h>
#include <stdlib.h>
#include "nifty.h"
#include "amiga.h"
#include "display.h"
#include "nio.h"

/* Correct prototype for the CheckIO.
 * (The one in clib/exec_protos.h has wrong return value type: BOOL (16 bits) 
 * instead of a pointer (32 bits)!)
 */
struct IORequest * CheckIO(struct IORequest *req);

#if USE_RLOGIN
#if __SASC
#include <proto/socket.h>
#elif __GNUC__
#include <inline/socket.h>
#else
#include <clib/socket_protos.h>
#endif
#include "rlogin.h"
#endif

extern const char version[];
static char copyright[] =
"Original NiftyTerm © Copyright 1990 by Chris Newman and Todd Williamson.\n"
"All Rights Reserved.\n"
"Network and International version © Copyright 1991, 1993 by Pekka Pessi.\n"
"For full copyright information see supporting documentation.\n"
"This is a nifty program.\n";

/* Amiga specific stuff */
#define BTOC(bptr) ((void *)((long)(bptr) << 2))
struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct DiskfontBase *DiskfontBase = NULL;
extern struct Window *w;
struct timerequest *tr = NULL;
struct MsgPort *tp = NULL;
long _stack = 8192;
long _priority = 0;
long _BackGroundIO = 1;
char *_procname = PROGNAME;
long channel;

/* emulation routines */
#if 0
extern char *gettermcap(), *gettermtype();
#endif

/* preferences routines */
extern char *getprofile();

/* misc.c routines */
extern char *itos();

/* keymap.c routine */
struct KeyMap *FindKeyMap(char *);

/* imported globals */
extern int emulation;	    /* emulation mode */
extern int ansi_LNM;	    /* ansi line feed/newline mode */
extern int bell_type;       /* 0 = none, 1 = visual, 2 = audio, 3 = both, 4 = displaybeep() */
extern int pass8;

BPTR logfile =	NULL;	/* optional log file */

/* Generalized I/O (input from almost anywhere!) */
union {
    struct FileHandle *fh;
    CHANN *chan;
    long sock;
} io = 0;

union {
    struct StandardPacket *dos;
    struct IOExtSer *dev;
} in = NULL, out = NULL;

struct MsgPort *iop = NULL;
long iomask;

enum iotype iotype = 
    (USE_SERIAL ? serial : (USE_DNET ? dnet : (USE_RLOGIN ? rlogin : stdio)));

char *bps = "38400";
 
int  
    show_version = 0, 
    wait_niftyterm = 0, 
    unit, shared = 0;

#if USE_SERIAL || USE_STDIO
#define INBUFSIZE 512
char inbuf[INBUFSIZE];
#endif

#if USE_RLOGIN
/* Quite a hefty buffer - 
 * we don't want to flush anything before OOB
 */
#define BUFSIZE 16324
#else
#define BUFSIZE 4096
#endif
char buf[BUFSIZE], device[64], altismeta = 1;
struct Device *ConsoleDevice = (struct Device *)NULL;
struct IOStdReq console_IO;
int cursormap;
struct Process *me;

void
_wb_parse(pp, WBenchMsg)
struct Process *pp;
struct WBStartup *WBenchMsg;
{
    return;
}

void
CloseConsole()
{
    if (ConsoleDevice) CloseDevice((struct IORequest *)&console_IO);
    ConsoleDevice = NULL;
}

/*
 * set io type depending of the device name
 */
void 
niotype(char *name)
{
    if (!strcmp(name, "dnet")) {
	iotype = dnet;
    } else if (!strcmp(name, "net")) {
	iotype = rlogin;
    } else {
	iotype = serial;
	device = strncpy(device, name, sizeof(device));
    }
}

void
nopen(char *host)
{
    me = (struct Process *)FindTask(0L);

    switch (iotype) {
#if USE_SERIAL
    case serial: 
	if (!(iop = CreateMsgPort()) ||
	    !(in.dev = (struct IOExtSer *)
	      CreateIORequest(iop, sizeof(*in.dev))) ||
	    !(out.dev = (struct IOExtSer *)
	      CreateIORequest(iop, sizeof(*out.dev))) ||
	    (in.dev->io_SerFlags |= SERF_RAD_BOOGIE|(shared ? SERF_SHARED : 0),
	     OpenDevice(device, unit, (struct IORequest *)in.dev, 0))) {
	    if (out.dev) DeleteIORequest(out.dev);
	    if (in.dev) DeleteIORequest(in.dev);
	    if (iop) DeleteMsgPort(iop);
	    fputs("Unable to open ", stderr);
	    fputs(device, stderr);
	    fputs(" unit ", stderr);
	    fputs(itos(unit), stderr);
	    fputs(".\n", stderr);
	    amigaquit();
	    exit(-1);
	}
	iomask = 1 << iop->mp_SigBit;
	*(out.dev) = *(in.dev);
	in.dev->IOSer.io_Command = CMD_READ;
	in.dev->IOSer.io_Data = (APTR)inbuf;
	in.dev->IOSer.io_Length = (ULONG)1;
	in.dev->IOSer.io_Flags = IOB_QUICK;
	SendIO((struct IORequest *)in.dev);
	break;
#endif
#if USE_STDIO
    case stdio: 
	io.fh = (struct FileHandle *)BTOC(me->pr_CIS);
	if (!(iop = CreateMsgPort()) ||
	    !(in.dos = (struct StandardPacket *)
	      CreateIORequest(iop, sizeof(*in.dos)))) {
	    if (in.dos) DeleteIORequest(in.dos);
	    if (iop) DeleteMsgPort(iop);
	    fputs("Trouble opening stdin\n", stderr);
	    amigaquit();
	    exit(-1);
	}
	iomask = 1 << iop->mp_SigBit;
	InitDosPkt(in.dos, inbuf, INBUFSIZE);
	PutMsg(io.fh->fh_Type, (struct Message *)in.dos);
	break;
#endif
#if USE_DNET
    case dnet: 
	io.chan = (CHANN *)DOpen(NULL, PORT_IALPHATERM, 20, 15);
	if(!io.chan) {
	    fputs("Unable to connect\n", stderr);
	    amigaquit();
	    exit(-1);
	}
	iomask = 1 << ((struct MsgPort *)io.chan)->mp_SigBit;
	DQueue(io.chan, 32);
	break;
#endif
#if USE_RLOGIN
    case rlogin:
	if (host == NULL) {
	    (void)fputs(PROGNAME ": no host specified.\n", stderr);
	    amigaquit();
	    exit(-1);
	}
	if (SocketBase = OpenLibrary("bsdsocket.library", 2L)) {
	    int true = 1;
	    struct servent *sp;
	    char *temp, *user;
	    char remotename[16];	/* hard limit of the rshd */
	    char term[16];

	    SetErrnoPtr(&errno, sizeof(errno));

	    IO_bit = AllocSignal(-1); 
	    URG_bit = AllocSignal(-1); 
      
	    iomask = 1 << IO_bit;
	    SIGURG = 1 << URG_bit;

	    SetSocketSignals(SIGBREAKF_CTRL_C, iomask, SIGURG|iomask);

	    sp = getservbyname("login", "tcp");
	    if (sp == NULL) {
		(void)fputs("login/tcp: unknown service.\n", stderr);
		amigaquit();
		exit(-1);
	    }
      
	    user = getenv("USER");
	    if (!user) user = "nobody";
	    temp = getprofile(PROGNAME ".remotename");
	    strncpy(remotename, temp ? temp : user, sizeof(remotename));

	    temp = getprofile(PROGNAME ".remotetype");
	    strcpy(term, temp ? temp : 
		    (emulation == EMU_H19 ? "h19/" :
		     (emulation == EMU_VT52 ? "vt52/" : "vt102/")));
	    /* sizeof("vt100") == 6 */
	    strncat(term, bps, sizeof(term) - 6);

	    io.sock = rcmd(&host, sp->s_port, user, remotename, term);
	    if (io.sock < 0) { amigaquit(); exit(-1); }

	    /* Set title to remote host name */
	    setbasetitle(host);

	    /* Set Signal driven IO */
#if USE_FIONBIO
	    ioctl(io.sock, FIONBIO, (char*)&true); 
#endif
	    ioctl(io.sock, FIOASYNC, (char*)&true);
	    true = IPTOS_LOWDELAY;
	    if (setsockopt(io.sock, IPPROTO_IP, IP_TOS, 
			   (char *)&true, sizeof(int)) < 0) {
	      perror(PROGNAME ": setsockopt TOS (ignored)");
	    }
	} else {
	    fputs("Unable to open BSD socket library.\n", stderr);
	    amigaquit();
	    exit(-1);
	}
	break;
#endif
    default:
	fputs("This version do not support ", stderr);
	fputs(iotype == serial ? "serial devices\n" : 
	      (iotype == stdio  ? "AmigaDOS IO\n" :
	       (iotype == dnet ? "dnet.\n" : "rlogin protocol\n")), stderr);
	exit(-1);
    }
}


void 
CloseNiftyIO(void) 
{
#if USE_RLOGIN
    CloseSocketBase();
#endif
}

#if USE_STDIO
void
InitDosPkt(pkt, buf, size)
struct StandardPacket *pkt;
char *buf;
long size;
{
    pkt->sp_Msg.mn_Node.ln_Name = (char *)&(pkt->sp_Pkt);
    pkt->sp_Pkt.dp_Link = &pkt->sp_Msg;
    pkt->sp_Pkt.dp_Port = iop;
    pkt->sp_Pkt.dp_Type = ACTION_READ;
    pkt->sp_Pkt.dp_Arg1 = io.fh->fh_Arg1;
    pkt->sp_Pkt.dp_Arg2 = (long)buf;
    pkt->sp_Pkt.dp_Arg3 = size;
}
#endif

long
nnread(buf, length)
char *buf;
long length;
{
    switch (iotype) {
#if USE_SERIAL
    case serial: {
	long i, ret, save;
	if(GetMsg(iop)) {
	    buf[0] = inbuf[0] & 127;
	    in.dev->IOSer.io_Command = SDCMD_QUERY;
	    DoIO((struct IORequest *)in.dev);
	    save = in.dev->IOSer.io_Actual;
	    if(ret = MIN(in.dev->IOSer.io_Actual, length)) {
	        in.dev->IOSer.io_Command = CMD_READ;
	        in.dev->IOSer.io_Data = (APTR)(buf+1);
	        in.dev->IOSer.io_Length = (ULONG)ret;
	        DoIO((struct IORequest *)in.dev);
		if(in.dev->IOSer.io_Error) {
		    if(in.dev->IOSer.io_Error == 12) 
			termout("\n**** Serial Buffer Overflow;  CLEARING BUFFERS\n", 50);
		    else
			termout("**** Serial Device Error; CLEARING BUFFERS\n", 43);
		    in.dev->IOSer.io_Command = CMD_CLEAR;
		    DoIO((struct IORequest *)in.dev);
		}
		ret = in.dev->IOSer.io_Actual;
#if 0
		for(i=1; i<=ret; i++) 
		    buf[i] = buf[i] & 127;
#endif
            }
	    in.dev->IOSer.io_Command = CMD_READ;
	    in.dev->IOSer.io_Data = (APTR)inbuf;
	    in.dev->IOSer.io_Length = (ULONG)1;
	    SendIO((struct IORequest *)in.dev);
	    return ret + 1;
	}
	else return 0;
    }
#endif
#if USE_STDIO
    case stdio: {
	long ret;
        GetMsg(iop);
	ret = in.dos->sp_Pkt.dp_Res1;
	if(ret) 
	    CopyMem(inbuf, buf, ret);
	else ret = -1;
	InitDosPkt(in.dos, inbuf, INBUFSIZE);
	PutMsg(io.fh->fh_Type, (struct Message *)in.dos);
	return ret;
    }
#endif
#if USE_DNET
    case dnet:
	return DNRead(io.chan, buf, length);
#endif
#if USE_RLOGIN
    case rlogin: {
	long n = 0, m; 
	if (SetSignal(0L, SIGURG) & SIGURG) {
	    n = rread(io.sock, buf, length);
	    if (n >= 0) 
		SetSignal(1 << IO_bit, 1 << IO_bit);
	    return n;
	} 

	for (;;) {
#if USE_FIONBIO
	    m = recv(io.sock, buf + n, length - n, 0);
	    if (m < 0) {
		if (errno == EWOULDBLOCK) {
		    return n;
		} else {
		    return m;
		}
	    } else if (m == 0) {
		/* This may be caused by the remote socket shutdown 
		 * or OOB data  delivered to the socket.
                 */
		ioctl(io.sock, SIOCATMARK, &m);
		if (m) { 
		    SetSignal(SIGURG, SIGURG);
		}
		return n;
	    }
	    n += m;
#else
	    /* Find out how many bytes there are waiting for us */
	    if (ioctl(io.sock, FIONREAD, (char*)&m) < 0) {
		perror("nnread FIONREAD");
		return -1;
	    }
	    if (n + m > length) {
		SetSignal(1 << IO_bit, 1 << IO_bit);
		m = length - n;
	    }
	    if (m == 0) 
		return n;
	    m = recv(io.sock, buf + n, m, 0);
	    if (m > 0) {
		n += m;
	    } else {
		if (errno == EWOULDBLOCK) 
		    return n;
		else
		    return m;	/* error */
	    }
#endif
	}
    }
#endif
    }
}

long
nwrite(buf, length)
char *buf;
long length;
{
    switch (iotype) {
#if USE_SERIAL
    case serial:
        out.dev->IOSer.io_Command = CMD_WRITE;
	out.dev->IOSer.io_Data = (APTR)buf;
	out.dev->IOSer.io_Length = (ULONG)length;
	DoIO((struct IORequest *)out.dev);
	return (long)out.dev->IOSer.io_Actual;
#endif
#if USE_STDIO
    case stdio:
    	return Write(me->pr_COS, buf, length);
#endif
#if USE_DNET
    case dnet:
        return DWrite(io.chan, buf, length);
#endif
#if USE_RLOGIN
    case rlogin:
	return send(io.sock, buf, length, 0);
#endif
    }
}

void 
niosize(unsigned short ws_row, unsigned short ws_col, 
	unsigned short ws_xpixel, unsigned short ws_ypixel)
{
    switch (iotype) {
#if USE_DNET	
    case dnet:
	DIoctl(io.chan, CIO_SETROWS, ws_row, 0);
       	DIoctl(io.chan, CIO_SETCOLS, ws_col, 0);
	break;
#endif
#if USE_RLOGIN
    case rlogin:
	winsize.ws_row    = htons(ws_row);
	winsize.ws_col    = htons(ws_col);
	winsize.ws_xpixel = htons(ws_xpixel);
	winsize.ws_ypixel = htons(ws_ypixel);

	if (okwinch) rsendwsize(io.sock);
	
	break;
#endif
    }
}

void
nioctl(code, arg1, arg2)
{
    switch (iotype) {
#if USE_SERIAL
    case serial:
	if(code == CIO_FLUSH) {
	    out.dev->IOSer.io_Command = CMD_CLEAR;
	    DoIO((struct IORequest *)out.dev);
	}
	else if(code == CIO_BREAK) {
	    out.dev->IOSer.io_Command = SDCMD_BREAK;
	    DoIO((struct IORequest *)out.dev);
	}
	break;
#endif
#if USE_STDIO
    case stdio:
	break;
#endif
#if USE_DNET
    case dnet:
	DIoctl(io.chan, code, arg1, arg2);
	break;
#endif
#if USE_RLOGIN
    case rlogin:
	break;
#endif
    }
}

ngetioctl(val, aux)
short *val;
char *aux;
{
    switch (iotype) {
#if USE_SERIAL
    case serial:
	break;
#endif
#if USE_STDIO
    case stdio:
	break;
#endif
#if USE_DNET
    case dnet:
	return DGetIoctl(io.chan, val, aux);
#endif
#if USE_RLOGIN
    case rlogin: 
	break;
#endif
    }
    return -1;
}

/* For unlisten mode.  No serial activity may occur after this call until 
 * nunabort() */
int nabort() 
{
#if USE_SERIAL
    if(iotype == serial) {
	AbortIO((struct IORequest *)in.dev);
	WaitIO((struct IORequest *)in.dev);
	return 1;
    }
#endif
    return 0;
}

/* undoes nabort().  */
int nunabort()
{
#if USE_SERIAL
    if(iotype == serial) {
	in.dev->IOSer.io_Command = CMD_READ;
	in.dev->IOSer.io_Data = (APTR)inbuf;
	in.dev->IOSer.io_Length = (ULONG)1;
	in.dev->IOSer.io_Flags = IOB_QUICK;
	SendIO((struct IORequest *)in.dev);
	return 1;
    }
#endif
    return 0;
}

void
nclose(void)
{
    switch (iotype) {
#if USE_SERIAL
    case serial:
	AbortIO((struct IORequest *)in.dev);
	WaitIO((struct IORequest *)in.dev);
	CloseDevice((struct IORequest *)in.dev);
	DeleteIORequest((struct IORequest *)in.dev);
	DeleteIORequest(out.dev);
	DeleteMsgPort(iop);
	break;
#endif
#if USE_STDIO
    case stdio:
    	WaitPort(iop);
	GetMsg(iop);
	DeleteIORequest(in.dos);
	DeleteMsgPort(iop);
	break;
#endif
#if USE_DNET
    case dnet:
        DClose(io.chan);
	break;
#endif
#if USE_RLOGIN
    case rlogin:
	CloseSocket(io.sock);
#endif
    }
}
    
/* die
 */
void 
fatalError(s)
char *s;
{
    fputs(PROGNAME ": ", stderr);
    fputs(s, stderr);
    fputc('\n', stderr);
    dsquit();
    amigaquit();
    exit(-1);
}

/* read in assorted preferences
 */
void 
readPreferences()
{
    char    *temp;

    if ((temp = getprofile(PROGNAME ".emulation")) != NULL) {
	if (*temp =='v' && temp[1] == 't' && temp[2] == '5')
	    emulation = EMU_VT52;
	else if (*temp == 'h' && temp[1] == '1')
	    emulation = EMU_H19;
    }
    if ((temp = getprofile(PROGNAME ".bell")) != NULL) {
	if (*temp == 'a')
	    bell_type =	2;  /* audio bell */
	else if (*temp == 'v')
	    bell_type =	1;  /* visual bell */
	else if (*temp == 'b')
	    bell_type = 3;  /* both */
	else if (*temp == 'd')
	    bell_type = 4;  /* call displaybeep() for bell */
	else
	    bell_type =	0;  /* no bell */
    }
    if((temp = getprofile(PROGNAME ".device")) != NULL) {
	niotype(temp);
    }
    if (temp = getprofile(PROGNAME ".linespeed")) {
	char *newbps = malloc(strlen(temp) + 1);
	if (newbps) 
	    bps = strcpy(newbps, temp);
    }

    pass8 = getprofileswitch(PROGNAME ".pass8", 1);
    altismeta = getprofileswitch(PROGNAME ".altismeta", 0);
}

/* parse the argument list
 */
#define ARGUMENT (*cont ? cont : (argv++, next))
char **parseargs(argv)
char **argv;
{
    char    *cont, *next;
    int	    args;

    /* loop through until end of list or non-switch argument */
    while (*argv != NULL && **argv == '-' && (*argv)[1]) {
	next = *(argv+1);
	cont = *argv+2;
	switch ((*argv)[1]) {
	case 'B':
	    bps = ARGUMENT;
	    break;

	case '7':
	    pass8 = 0;
	    break;

	case 'd':
	    niotype(ARGUMENT);
	    break;
		
	case 'h':
	    if((*argv)[2] == '1')
		emulation = EMU_H19;
	    else
		goto CHECKWM;
	    break;

	case 's':
	    if ((*argv)[2] == 't') {
		wait_niftyterm = 1;
		iotype = stdio;
	    } else if(!strcmp(ARGUMENT, "hared"))
		shared =  1;
	    else
		goto CHECKWM;
	    break;

	case 'l':
	    logfile = Open(ARGUMENT, MODE_NEWFILE);
	    break;

	case 'u':
	    unit = atoi(ARGUMENT);
	    break;
		
	case 'v':
	    if ((*argv)[2] != 't')
		goto CHECKWM;
	    if ((*argv)[3] == '1')
		emulation = EMU_VT102;
	    else if ((*argv)[3] == '5')
		emulation = EMU_VT52;
	    else
		goto CHECKWM;
	    break;

	case 'V':
	    show_version = 1;
	    break;

	case 'w':
	    wait_niftyterm = 1;
	    break;

	case 'N':
	    channel = atoi(ARGUMENT);
	    break;

	default:
	CHECKWM:
	    args = dsparsearg((*argv)[1], cont, next);
	    if (!args)
		goto USAGE;
	    argv+=args-1;
	    break;
	}
	argv++;
    }

    return (argv);

    USAGE:
    puts("Usage: " PROGNAME " [-option ...]\n"
	 "Available " PROGNAME " options are:\n"
	 "  -V              show version number & copyright notice\n"
	 "  -h19            emulate an h19 instead of a vt102 terminal\n"
	 "  -vt52           emulate a vt52 on startup\n"
	 "  -vt102          emulate a vt102 on startup (default emulation)\n"
	 "  -l <file>       write all output to a log file\n"
	 "  -st             take input from stardard input.\n"
	 "  -d <devicename> specify device to use (e.g. serial.device)\n"
	 "  -u <unit #>     specify unit number to use (see -d)\n"
	 "  -B <linespeed>  specify line speed to use\n"
         "  -N <net>        give DNet network number\n"
         "  -shared         open device in shared mode\n"
         "  -7              strip 8th bit of character codes\n"
	 "  -w              wait after program ends before closing window.");
    dsshowusage();
    exit(0);
    /*NOTREACHED*/
}

/* This allows terminal answerback messages
 */
int write_to_tty(buf, count)
char	*buf;
int	count;
{
    if (iotype != stdio)
	return (nwrite(buf, count));
    dsputs(buf);
    return (count);
}

void
amigainit(void)
{
    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
    DiskfontBase = (struct DiskfontBase *)OpenLibrary("diskfont.library", 0);
    if (!GfxBase || !IntuitionBase || !DiskfontBase ||
	!(tp = CreateMsgPort()) ||
	!(tr = (void *)CreateIORequest(tp, sizeof(*tr)))) {
        puts("Problem during initialization.\n"
	     "A needed library or resource was not found.\n");
	amigaquit();
    }
    if(OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)tr, 0)) {
        puts("Problem opening timer device");
	amigaquit();
	exit(-1);
    }    
}

void
amigaquit(void)
{
    if(tr) {
        CloseDevice((struct IORequest *)tr);
        DeleteIORequest((struct IORequest *)tr);
    }
    if (tp) DeleteMsgPort(tp);

    CloseNiftyIO();

    CloseConsole();
    if(GfxBase) CloseLibrary(GfxBase);
    if(IntuitionBase) CloseLibrary(IntuitionBase);
    if(DiskfontBase) CloseLibrary(DiskfontBase);
    if(logfile) Close(logfile);
}

/* Event handler for intuition events.
 */
void
do_IntuiMessage()
{
    struct IntuiMessage *msg;
    int count;
    
    while(msg = (struct IntuiMessage *)GetMsg(w->UserPort)) {
        switch(msg->Class) {
	    case RAWKEY:
	        do_RawKey(msg);
		break;
            case CLOSEWINDOW:
                ReplyMsg((struct Message *)msg);
		nclose();
                dsquit();
	        amigaquit();
	        exit(0);
	        break;
	    case NEWSIZE:
		ReplyMsg((struct Message *)msg);
                redraw_display(1);
		break;
	    case MENUPICK:
	        if(count = domenu(msg, buf, sizeof(buf)))
		    nwrite(buf, count);
		break;
	    case MOUSEMOVE:
	    case MOUSEBUTTONS:
	        if(count = domouse(msg, buf, sizeof(buf)))
		    nwrite(buf, count);
		ReplyMsg((struct Message *)msg);
		break;
#ifdef OWNICONIFY
	    case GADGETDOWN:
	        if(DoubleClick(secs, mics, msg->Seconds, msg->Micros)) {
		    ReplyMsg((struct Message *)msg);
		    while(msg = (struct IntuiMessage *)GetMsg(w->UserPort)) ReplyMsg((struct Message *)msg);
		    deiconify();
   	            mapwindow();
		}
		else {
                    secs = msg->Seconds; mics = msg->Micros;
		    ReplyMsg((struct Message *)msg);
		}
		break; 
#endif
            default:
                ReplyMsg((struct Message *)msg);
        }
    }
}

void 
HandleSetWinSize(struct Window *win, short rows, short cols)
{
    short height, width;
    short dx, dy;
    
    width = cols*win->RPort->TxWidth + win->BorderLeft + win->BorderRight;
    height = rows*win->RPort->TxHeight+win->BorderTop+win->BorderBottom;
    dx = win->WScreen->Width - (win->LeftEdge+width);
    if(dx > 0)
	dx = 0;
    if(-dx > win->LeftEdge) {
	dx = -win->LeftEdge;
	width = win->WScreen->Width;
    }
	    
    dy = win->WScreen->Height - (win->TopEdge + height);
    if(dy > 0) 
	dy = 0;
    if(-dy > win->TopEdge) {
	dy = -win->TopEdge;
	height = win->WScreen->Height;
    }
	    
    if(dx || dy) {
	MoveWindow(win, dx, dy);
    }
    if(win->Width != width || win->Height != height) {
	SizeWindow(win, width - win->Width, height - win->Height);
    }
}

void
HandleIoctl(cmd, val, aux, win)
short cmd, val;
char aux;
struct Window *win;
{
#if USE_DNET
    static short saverows;

    if (iotype == dnet) {
	switch (cmd) {
#if 0
	case CIO_MODE:
	    Cooked = val;
	    break;
#endif
	case CIO_SETROWS:
	    saverows = val;
	    return;
	case CIO_SETCOLS:
	    HandleSetWinSize(win, saverows, val);
	    return;
	}
    } 
#endif
}

void
do_IOinput()
{
    int n;

    if((n = nnread(buf, BUFSIZE)) > 0) {
	if(logfile != (BPTR) NULL && n && (Write(logfile, buf, n) != n)) {
	    Close(logfile);
	    logfile = (BPTR) NULL;
	    fputs("Error writing to log file", stderr);
	}
	termout(buf, n);
    }
    else if (n == -2) {
	short val, cmd;
	char aux;
	cmd = ngetioctl(&val, &aux);
	if (cmd != -1)
	    HandleIoctl(cmd, val, aux, w);
    }
    else if(n < 0) {
	if(wait_niftyterm) {
	    dscursoroff();
	    dscursormove(NEXT_LINE, 1);
	    (void) dsstyle(INVERSE1);
	    dsputs("** Press any key to exit. **");
	    dscursoron();
	    WaitPort(w->UserPort);
	    while(GetMsg(w->UserPort));
	}
	dsquit();
	amigaquit();
	exit(0);
    }
}

/*******************
 * niftyterm entry point
 */
/*ARGSUSED*/
main(argc, argv)
int	argc;
char	**argv;
{
    long winmask, timemask, mask;

    amigainit();
    dsinit();
    readPreferences();
    argv = parseargs(argv+1);
    if (iotype == stdio) {
	if (show_version) {
	    puts(version);
	    puts(copyright);
	}
    } 

    ansi_LNM = iotype == serial;

    /* nopen() should be called before dsstart() because it sets the title.. */
    nopen(*argv); 
    dsstart();
    timemask = 1 << tp->mp_SigBit;
    
    for (;;) {
	dscheckflash();

        tr->tr_time.tv_secs = 1;
        tr->tr_time.tv_micro = 0;
	tr->tr_node.io_Command = TR_ADDREQUEST;
	SendIO((struct IORequest *)tr);
        winmask = 1 << w->UserPort->mp_SigBit;
	mask = Wait(iomask | winmask | timemask);

	if(!((mask & timemask)&&(CheckIO((struct IORequest *)tr)))) 
	    AbortIO((struct IORequest *)tr);
        WaitIO((struct IORequest *)tr);
	SetSignal(0, timemask); /* In case WaitIO didn't do it for me... */

	if(mask & winmask) {
	    do_IntuiMessage();
	}

	if(mask & iomask) {
	    do_IOinput();
        }
    }	
    /*NOTREACHED*/
}
