
/*
 *  DNETLIB.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  Library Interface for DNET.
 */

#define NOEXT
#include "/dnet/dnet.h"
#include "/lib/dnetlib.h"
#include <stdio.h>

#define CHANN	struct _CHANN

#define NAMELEN sizeof("DNET.PORT.XXXXX")
#define NAMEPAT "DNET.PORT.%ld"

CHANN {
    PORT    port;	    /*	receive data, replies	    */
    PORT    *dnetport;	    /*	dnet's master port          */
    LIST    rdylist;	    /*	ready to be read	    */
    uword   chan;	    /*	channel # for open channels */
    ubyte   eof;	    /*	channel remotely closed/eof */
    ubyte   filler;
    int     qlen;	    /*	allowed write queue size    */
    int     queued;	    /*	current # packets queued    */
};

/*BREAKUP   vers.bc	*/

Version(name, ver, subver)
char *name;
char *ver;
char *subver;
{
    Write(Output(), name, strlen(name));
    Write(Output(), " V", 2);
    Write(Output(), ver, strlen(ver));
    Write(Output(), subver, strlen(subver));
    Write(Output(), "\n", 1);
}

/*BREAKUP   dlis.bc	*/


PORT *
DListen(portnum)
uword portnum;
{
    PORT *port = NULL;
    char *ptr;

    ptr = AllocMem(NAMELEN, MEMF_PUBLIC);   /*  memory the the name     */
    sprintf(ptr, NAMEPAT, portnum);
    Forbid();                               /*  task-atomic operation   */
    if (FindPort(ptr) || !(port = CreatePort(ptr,0)))
	FreeMem(ptr, NAMELEN);
    Permit();
    return(port);
}

/*BREAKUP   dunl.bc	*/

void
DUnListen(lisport)
PORT *lisport;
{
    if (lisport) {
	register char *ptr = lisport->mp_Node.ln_Name;
	Forbid();                       /*  task-atomic operation       */
	while (DNAAccept(lisport));     /*  remove all pending requests */
	DeletePort(lisport);            /*  gone!                       */
	Permit();
	FreeMem(ptr, NAMELEN);
    }
}

/*BREAKUP   dacc.bc	*/


/*
 *  DAccept()
 *
 *  Note:   This call will work even if called by a task which does not
 *	    own the listen port.
 */

PORT *
DAccept(lisport)
PORT *lisport;
{
    register IOR *ior;
    register CHANN *chan = NULL;
    CHANN *MakeChannel();

    while (!chan && (ior = GetMsg(lisport))) {
	switch(ior->io_Command) {
	case DNCMD_SOPEN:
	    chan = MakeChannel(ior, NULL);
	    break;
	default:
	    ior->io_Error = 1;
	    break;
	}
	ReplyMsg(ior);
    }
    if (lisport->mp_MsgList.lh_Head != (NODE *)&lisport->mp_MsgList.lh_Tail)
	SetSignal(1 << lisport->mp_SigBit, 1 << lisport->mp_SigBit);
    return((PORT *)chan);
}

/*BREAKUP   dnaac.bc	*/


/*
 *  Refuse a connection
 */

DNAAccept(lisport)
PORT *lisport;
{
    IOR *ior;

    if (ior = GetMsg(lisport)) {
	ior->io_Error = 1;
	ReplyMsg(ior);
    }
    if (lisport->mp_MsgList.lh_Head != (NODE *)&lisport->mp_MsgList.lh_Tail)
	SetSignal(1 << lisport->mp_SigBit, 1 << lisport->mp_SigBit);
    return(ior != NULL);
}

/*BREAKUP   dpri.bc	*/


DPri(chan, pri)
CHANN *chan;
{
}

/*BREAKUP   dopen.bc	*/

PORT *
DOpen(host, portnum, txpri, rxpri)
char *host;
char txpri, rxpri;
uword portnum;
{
    IOR ior;
    CHANN *chan;
    CHANN *MakeChannel();

    if (!host)
	host = "0";
    chan = MakeChannel(&ior, host);
    if (rxpri > 126)
	rxpri = 126;
    if (rxpri < -127)
	rxpri = -127;
    if (txpri > 126)
	txpri = 126;
    if (txpri < -127)
	txpri = -127;
    if (chan->dnetport) {
	ior.io_Command = DNCMD_OPEN;
	ior.io_Unit = (void *)portnum;
	ior.io_Offset = (long)chan;
	ior.io_Message.mn_ReplyPort = (PORT *)chan;
	ior.io_Message.mn_Node.ln_Pri = txpri;
	ior.io_Message.mn_Node.ln_Name= (char *)rxpri;

	PutMsg(chan->dnetport, &ior);
	WaitMsg(&ior);
	if (ior.io_Error == 0) {
	    chan->chan = (long)ior.io_Unit;
	    FixSignal(chan);
	    return((PORT *)chan);
	}
    }
    DeleteChannel(chan);
    return(NULL);
}

/*BREAKUP   dceof.bc	*/


DCheckEof(chan)
CHANN *chan;
{
    return (chan->eof);
}

/*BREAKUP   dgioc.bc	*/

DGetIoctl(chan, pval, paux)
CHANN *chan;
short *pval;
char *paux;
{
    register IOR *ior;
    short cmd = -1;

    if ((ior = GetHead(&chan->rdylist)) || (ior = GetHead(&chan->port.mp_MsgList))) {
	if (ior->io_Command == DNCMD_IOCTL) {
	    CIOCTL *cio = (CIOCTL *)ior->io_Data;
	    cmd = cio->cmd;
	    *pval = (cio->valh << 8) | cio->vall;
	    *paux = cio->valaux;
	    Forbid();
	    Remove(ior);
	    ReplyMsg(ior);
	    Permit();
	}
    }
    FixSignal(chan);
    if (chan->eof)
	SetSignal(1 << chan->port.mp_SigBit, 1 << chan->port.mp_SigBit);
    return(cmd);
}

/*BREAKUP   dnread.bc	 */


DNRead(chan, buf, bytes)
CHANN *chan;
char *buf;
{
    register IOR *ior;
    int len = 0;
    long n;

    if (chan->eof)
	return(-1);
    while (bytes && ((ior = RemHead(&chan->rdylist)) || (ior = GetMsg(chan)))) {
#ifdef DEBUG
	printf("IOR %08lx cmd %d len %d act %d\n", ior, ior->io_Command, ior->io_Length, ior->io_Actual);
#endif
	if (ior->io_Message.mn_Node.ln_Type == NT_REPLYMSG) {
	    if (!chan->queued)
		puts("DNRead: Software Error");
	    else
		--chan->queued;
	    if (ior->io_Length)
		FreeMem(ior->io_Data, ior->io_Length);
	    FreeMem(ior, sizeof(IOR));
	    continue;
	}
	switch(ior->io_Command) {
	case DNCMD_CLOSE:
	case DNCMD_EOF:
	    chan->eof = 1;
	    ReplyMsg(ior);
	    break;
	case DNCMD_IOCTL:
	    if (ior->io_Message.mn_Node.ln_Type == NT_REQUEUE)
		AddHead(&chan->rdylist, ior);
	    else
		AddTail(&chan->rdylist, ior);
	    ior->io_Message.mn_Node.ln_Type = NT_REQUEUE;
	    if (len == 0)
		len = -2;
	    goto done;
	case DNCMD_WRITE:
#ifdef DEBUG
	    printf("IOR LEN/ACT %ld/%ld\n", ior->io_Length, ior->io_Actual);
#endif
	    n = ior->io_Length - ior->io_Actual;
	    if (n < 0)
		puts("len fail");
	    if (n <= bytes) {
		CopyMem((char *)ior->io_Data + ior->io_Actual, buf, n);
		bytes -= n;
		len += n;
		buf += n;
		ReplyMsg(ior);
	    } else {
		CopyMem((char *)ior->io_Data + ior->io_Actual, buf, bytes);
		len += bytes;
		ior->io_Actual += bytes;
		bytes = 0;
		if (ior->io_Message.mn_Node.ln_Type == NT_REQUEUE)
		    AddHead(&chan->rdylist, ior);
		else
		    AddTail(&chan->rdylist, ior);
		ior->io_Message.mn_Node.ln_Type = NT_REQUEUE;
	    }
	    break;
	default:
	    ior->io_Error = 1;
	    ReplyMsg(ior);
	}
    }
#ifdef DEBUG
    puts("DONE1");
#endif
done:
    FixSignal(chan);
    if (chan->eof)
	SetSignal(1 << chan->port.mp_SigBit, 1 << chan->port.mp_SigBit);
#ifdef DEBUG
    printf("RETURN %ld\n", len);
#endif
    return(len);
}

/*BREAKUP   dread.bc	*/


DRead(chan, buf, bytes)
char *buf;
CHANN *chan;
{
    long len = 0;
    long n = -1;

    if (chan->eof)
	return(-1);
    while (bytes) {
	if (n == 0)
	    WaitPort(chan);
	n = DNRead(chan, buf, bytes);
	if (n < 0)
	    break;
	len += n;
	buf += n;
	bytes -= n;
	if (chan->eof)
	    break;
    }
    return(len);
}

/*BREAKUP   dq.bc	*/


DQueue(chan, n)
CHANN *chan;
{
    chan->qlen = n;
}

/*BREAKUP   dwr.bc	*/


DWrite(chan, buf, bytes)
CHANN *chan;
{
    int error = bytes;

    if (chan->qlen) {
	if (WaitQueue(chan, NULL) >= 0) {
	    register IOR *ior = AllocMem(sizeof(IOR), MEMF_CLEAR|MEMF_PUBLIC);
	    ior->io_Command = DNCMD_WRITE;
	    ior->io_Unit = (void *)chan->chan;
	    ior->io_Offset = (long)chan;
	    ior->io_Message.mn_ReplyPort = (PORT *)chan;
	    ior->io_Data = AllocMem(bytes, MEMF_PUBLIC);
	    ior->io_Length = bytes;
	    CopyMem(buf, ior->io_Data, bytes);
	    PutMsg(chan->dnetport, ior);
	    ++chan->queued;
	} else {
	    error = -1;
	}
    } else {
	IOR ior;
	ior.io_Command = DNCMD_WRITE;
	ior.io_Unit = (void *)chan->chan;
	ior.io_Offset = (long)chan;
	ior.io_Message.mn_ReplyPort = (PORT *)chan;
	ior.io_Data = (APTR)buf;
	ior.io_Length = bytes;
	PutMsg(chan->dnetport, &ior);
	WaitMsg(&ior);
	if (ior.io_Error)
	    error = -1;
    }
    FixSignal(chan);
    return(error);
}

/*BREAKUP   deof.bc	*/


DEof(chan)
CHANN *chan;
{
    IOR ior;

    ior.io_Command = DNCMD_EOF;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    PutMsg(chan->dnetport, &ior);
    WaitMsg(&ior);
    FixSignal(chan);
}

/*BREAKUP   dioc.bc	*/


DIoctl(chan, cmd, val, aux)
CHANN *chan;
ubyte cmd;
uword val;
ubyte aux;
{
    IOR ior;

    ior.io_Command = DNCMD_IOCTL;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    ior.io_Data = (APTR)(long)((val<<16)|(aux<<8)|cmd);
    PutMsg(chan->dnetport, &ior);
    WaitMsg(&ior);
    FixSignal(chan);
}

/*BREAKUP   dst.bc	*/


DStat(host, buffer, max)
char *host;
APTR buffer;
{
    IOR ior;
    char buf[sizeof(DNETPORTNAME)+32];
    PORT *replyport = CreatePort(NULL, 0);
    PORT *dnetport;

    if (!host)
	host = "0";
    sprintf(buf, "%s%s", DNETPORTNAME, host);
    if (dnetport = FindPort(buf)) {
	ior.io_Command = DNCMD_INFO;
	ior.io_Unit = 0;
	ior.io_Offset = 0;
	ior.io_Data = buffer;
	ior.io_Message.mn_ReplyPort = replyport;
	PutMsg(dnetport, &ior);
	WaitMsg(&ior);
	DeletePort(replyport);
    }
    return(dnetport != NULL);
}

/*BREAKUP   dqu.bc	*/


DQuit(host)
char *host;
{
    IOR ior;
    char buf[sizeof(DNETPORTNAME)+32];
    PORT *replyport = CreatePort(NULL, 0);
    PORT *dnetport;

    if (!host)
	host = "0";
    sprintf(buf, "%s%s", DNETPORTNAME, host);
    if (dnetport = FindPort(buf)) {
	ior.io_Command = DNCMD_QUIT;
	ior.io_Unit = 0;
	ior.io_Offset = 0;
	ior.io_Message.mn_ReplyPort = replyport;
	PutMsg(dnetport, &ior);
	WaitMsg(&ior);
	DeletePort(replyport);
    }
    return(dnetport != NULL);
}

/*BREAKUP   dcl.bc	*/


DClose(chan)
CHANN *chan;
{
    IOR ior;
    IOR *io;

    ior.io_Command = DNCMD_CLOSE;
    ior.io_Unit = (void *)chan->chan;
    ior.io_Offset = (long)chan;
    ior.io_Message.mn_ReplyPort = (PORT *)chan;
    PutMsg(chan->dnetport, &ior);
    ++chan->queued;
    chan->qlen = 0;
    WaitQueue(chan, &ior);
    while ((io = RemHead(&chan->rdylist)) || (io = GetMsg(chan))) {
	io->io_Error = 1;
	ReplyMsg(io);
    }
    DeleteChannel(chan);
}

/*BREAKUP   wq.bc	*/

WaitQueue(chan, skipior)
CHANN *chan;
IOR *skipior;
{
    register IOR *io;
    short error = 0;
    while (chan->queued > chan->qlen) {     /*  until done  */
	WaitPort(chan);                     /*  something   */
	io = (IOR *)GetMsg(chan);
	if (io->io_Message.mn_Node.ln_Type == NT_REPLYMSG) {
	    if (error == 0)
		error = io->io_Error;
	    if (io != skipior) {
		if (io->io_Length)
		    FreeMem(io->io_Data, io->io_Length);
		FreeMem(io, sizeof(IOR));
	    }
	    --chan->queued;
	} else {
	    AddTail(&chan->rdylist, io);
	}
    }
    return(error);
}

/*BREAKUP   mkchan.bc	*/


CHANN *
MakeChannel(ior, host)
register IOR *ior;
char *host;
{
    CHANN *chan = AllocMem(sizeof(CHANN), MEMF_PUBLIC|MEMF_CLEAR);

    /*	Name, Pri */
    chan->port.mp_Node.ln_Type = NT_MSGPORT;
    chan->port.mp_SigBit = AllocSignal(-1);
    chan->port.mp_SigTask = FindTask(NULL);
    NewList(&chan->port.mp_MsgList);
    NewList(&chan->rdylist);
    chan->chan = (long)ior->io_Unit;
    ior->io_Offset = (long)chan;
    if (host) {
	char buf[sizeof(DNETPORTNAME)+32];
	sprintf(buf, "%s%s", DNETPORTNAME, host);
	ior->io_Message.mn_ReplyPort = FindPort(buf);
    }
    chan->dnetport = ior->io_Message.mn_ReplyPort;
    return(chan);
}

/*BREAKUP   delchan.bc	  */


DeleteChannel(chan)
CHANN *chan;
{
    FreeSignal(chan->port.mp_SigBit);
    FreeMem(chan, sizeof(CHANN));
}

/*BREAKUP   fixsig.bc	*/

FixSignal(chan)
register CHANN *chan;
{
    if (chan->port.mp_MsgList.lh_Head != (NODE *)&chan->port.mp_MsgList.lh_Tail ||
    chan->rdylist.lh_Head != (NODE *)&chan->rdylist.lh_Tail)
	SetSignal(1 << chan->port.mp_SigBit, 1 << chan->port.mp_SigBit);
}

/*BREAKUP   getev.bc	*/

GetEnvVal(str)
char *str;
{
    char *ptr = GetDEnv(str);
    long val = 0;

    if (ptr)
	val = atoi(ptr);
    free(ptr);
    return(val);
}

/*BREAKUP   cfg.bc	*/

/*
 *  CONFIGURATION FILE EXTRACTION
 */

static FILE *Fi;

OpenCfgFile()
{
    CloseCfgFile();
    Fi = fopen("s:dnet.config", "r");
}

char *
GetCfgLine(what)
char *what;
{
    static char Buf[128];
    register char *ptr;
    if (Fi) {
	while (fgets(Buf, sizeof(Buf), Fi)) {
	    if (BCmp(Buf, what, 4) == 0) {
		Buf[strlen(Buf)-1] = 0;
		for (ptr = Buf + 4; *ptr == ' ' || *ptr == 9; ++ptr);
		return(ptr);
	    }
	}
    }
    return(NULL);
}

CloseCfgFile()
{
    if (Fi)
	fclose(Fi);
    Fi = NULL;
}

GetOneCfg(what)
char *what;
{
    char *str;
    OpenCfgFile();
    str = GetCfgLine(what);
    CloseCfgFile();
}

ExtractFieldVal(str, field, pidx)
char *str, *field;
short *pidx;
{
    short idx = (pidx) ? *pidx : 0;
    short flen = strlen(field);

    while (*str) {
	if (strncmp(str, field, flen) == 0) {
	    if (pidx)
		*pidx = idx + flen;	/*  past the field but not the val */
	    return(atoi(str + flen));
	}
	++str;
	++idx;
    }
    return(-1);
}

/*BREAKUP   prg.bc  */

char *Program = "";  /*  settable by other progs     */

/*BREAKUP   elog.bc	*/


_elog(line, file, how, ctl, data)
char *file;
char *ctl;
{
    static char *logfile;
    FILE *fi;

    if (logfile == NULL) {
	OpenCfgFile();
	logfile = GetCfgLine("LOGF");
	CloseCfgFile();
    }
    if (logfile && (fi = fopen(logfile, "a"))) {
	char *hdr = "?";
	switch(how) {
	case EFATAL:
	    hdr = "Fatal";
	    break;
	case EWARN:
	    hdr = "Warn";
	    break;
	case EDEBUG:
	    hdr = "Debug";
	    break;
	}
	fprintf(fi, "%s %s.%s/%ld ", hdr, Program, file, line);
	fprintf(fi, ctl, data);
	putc('\n', fi);
	fclose(fi);
    }
}

