/* begincopyright
  Copyright (c) 1988 Xerox Corporation. All rights reserved.
  Use and copying of this software and preparation of derivative works based
  upon this software are permitted. Any distribution of this software or
  derivative works must comply with all applicable United States export
  control laws. This software is made available AS IS, and Xerox Corporation
  makes no warranty about the software, its performance or its conformity to
  any specification. Any person obtaining a copy of this software is requested
  to send their name and post office or electronic mail address to:
    PCR Coordinator
    Xerox PARC
    3333 Coyote Hill Rd.
    Palo Alto, CA 94304
  endcopyright */
/*
 * CMUXGlue.c
 *
 * XR interface to Mentat Courier Mux
 *
 * Demers, April 25, 1990 9:11:44 am PDT
 */

#include <xr/BasicTypes.h>
#include <xr/Errno.h>
#include <xr/Threads.h>
#include <xr/UIO.h>

#include <sys/file.h>
#include <stropts.h>

#include "CMUX.h"
#include "CMUXGlue.h"



#include <xr/ThreadsMsgPrivate.h>


#define MAX←MSG←SEND←BYTES	512	/* the most I'll send at once */
#define MAX←MSG←RECV←BYTES	534	/* the most I'll recv at once */

#define GETMSG(d, cp, dp, f) \
	XR←GetMsg((d), (cp), (dp), (f))

#define PUTMSG(d, cp, dp, f) \
	XR←PutMsg((d), (cp), (dp), (f))

#define XR←GetEnv(s) \
	getenv(s)

/*
 * the following stuff belongs in CedarPreBasics or something ...
 */

typedef XR←Pointer (*XR←CProc)();

typedef struct XR←MProcRep {
    XR←CProc mp←proc;
    XR←Pointer mp←data;
} * XR←MProc;

XR←CProc
XR←CProcFromMProc(mp)
    XR←MProc mp;
{
    return mp->mp←proc;
}

XR←Pointer
XR←DataFromMProc(mp)
    XR←MProc mp;
{
    return mp->mp←data;
}

XR←MProc
XR←NewMProc(cp, data)
    XR←CProc cp;
    XR←Pointer data;
{
    XR←MProc mp = (XR←MProc)XR←malloc( sizeof(struct XR←MProcRep) );
    mp->mp←proc = cp;
    mp->mp←data = data;
    return mp;
}


/*
 * end of stuff to be moved
 */

/*
 * Debugging support
 */

static int XR←cmuxGlueDebugValue = 0;

int XR←CMUXDebug(delta)
    int delta;
{
    int ans = XR←cmuxGlueDebugValue;
    XR←cmuxGlueDebugValue += delta;
    return ans;
}


#define DebugMsg if(XR←cmuxGlueDebugValue != 0) XR←ConsoleMsg

XR←CMUXDescriptor
XR←CMUXCreate ()
{
    XR←CMUXDescriptor d;
    char * name;

    name = (char *)XR←GetEnv("COURIER←MUX←NAME");
    if( name == NIL ) {
	name = COURIER←MUX←NAME←DEF;
    }
    d = XR←OpenStream(name, O←RDWR, 0);
    XR←SetGetBlocking(d, XR←UIO←BLOCKING←ALL←DATA);
    if( d < 0 ) d = -(XR←GetErrno());
    DebugMsg("XR←CMUXCreate returns %d\n", d);
    return d;
}


void
XR←CMUXDestroy (d)
    XR←CMUXDescriptor d;
{
    DebugMsg("XR←CMUXDestroy %d\n", d);
    (void)XR←Close(d);
}



#define CONNECT←RETRIES		10
#define CONNECT←PAUSE←MSEC	300

int
XR←CMUXConnect(d, type, addr, addrLen)
    XR←CMUXDescriptor d;
    XR←CMUXAddrType type;
    XR←Pointer addr;
    int addrLen;
{
    char * buf = NIL;
    char lBuf[132];
    int i, ret, typeCode;
    struct strioctl si;

    if( (addr == NIL) || (addrLen == 0) ) { ret = -EINVAL; goto Out; }
    DebugMsg("CMUXConnect %d to ", d);
    for( i = 0; i < addrLen; i += sizeof(unsigned) ) {
        DebugMsg( "%d  ", *((unsigned *)(addr + i)) );
    }
    buf = lBuf;
    if( addrLen > ((sizeof lBuf) - (2*sizeof(short)))) {
        buf = (char *)XR←malloc(addrLen + (2*sizeof(short)));
        if( buf == NIL ) { ret = -ENOMEM; goto Out; }
    }

    for( i = 0; i <= CONNECT←RETRIES; i++ ) {
        ShortToBE16(addrLen, buf);
        buf += sizeof(short);
        switch(type) {
            case XR←CMUX←ADDR←TYPE←DFLT←NAME:
                typeCode = -COUR←DEF←ADDR←TYPE;
                break;
            case XR←CMUX←ADDR←TYPE←DFLT←ADDR:
                typeCode = COUR←DEF←ADDR←TYPE;
                break;
            case XR←CMUX←ADDR←TYPE←XNS←NAME:
                typeCode = -COUR←XNS←ADDR←TYPE;
                break;
            case XR←CMUX←ADDR←TYPE←XNS←ADDR:
                typeCode = COUR←XNS←ADDR←TYPE;
                break;
            default:
                ret = -EINVAL;
                goto Out;
        }
        ShortToBE16(typeCode, buf);
        buf += sizeof(short);
        (void)bcopy( ((char *)(addr)), buf, addrLen );
        buf -= 2*sizeof(short);
        si.ic←cmd = CMUX←ADDR←REQ;
        si.ic←len = addrLen + 2*sizeof(short);
        si.ic←dp = buf;
        si.ic←timout = -1;
        ret = XR←IOCtl(d, I←STR, &si);
        if( ret >= 0 ) break;

        ret = -(XR←GetErrno());
        DebugMsg("CMUXConnect IOCtl errno %d\n", -ret);
        if( (ret != -EINTR) || (typeCode > 0) ) break;
        (void)XR←PauseAbortable(XR←MsecToTicks(CONNECT←PAUSE←MSEC));
    }
  Out:
    if( (buf != lBuf) && (buf != NIL) ) XR←free(buf);
    XR←SetErrno(-ret);
    DebugMsg("CMUXConnect returns %d\n", ret);
    return ret;
}


int cmuxReadMsgDefaultClientBufLen = 2*MAX←MSG←RECV←BYTES;


int
XR←CMUXReadMsg(d, ignoreAborts, timeoutMsec, allocaterFunc, allocaterClientData)
    XR←CMUXDescriptor d;
    bool ignoreAborts;
    unsigned timeoutMsec;
    XR←CMUXAllocaterFunc allocaterFunc;
    XR←Pointer allocaterClientData;
{
    struct strbuf ctlBuf;
    struct strbuf dataBuf;
    int flags;
    char lBuf[MAX←MSG←RECV←BYTES];
    XR←Pointer clientBufPtr;
    int clientBufLen;
    int len, ret;
    int err = 0;
    long totalLen = 0;

    ctlBuf.buf = lBuf;
    ctlBuf.maxlen = (sizeof lBuf);

    clientBufLen = cmuxReadMsgDefaultClientBufLen; /* DEBUG */
    clientBufPtr = (*allocaterFunc)( 0, clientBufLen, allocaterClientData );
    if( clientBufPtr == NIL ) {
        XR←SetErrno(ENOMEM);
        return(-ENOMEM);
    }

    dataBuf.buf = ((char *)(clientBufPtr));
    dataBuf.maxlen = clientBufLen;

    if( timeoutMsec != XR←WAIT←FOREVER←MSEC ) {
        (void) XR←SetGetTimeout(d, timeoutMsec);
    }

    for (;;) {
        flags = 0;
        dataBuf.len = ctlBuf.len = 0;
        ret = GETMSG(d, &ctlBuf, &dataBuf, &flags);
        if( ret < 0 ) {
            DebugMsg("CMUXReadMsg botch 0\n");
            err = XR←GetErrno(); break;
        }
        len = dataBuf.len;
        if( len == -1 ) {
            DebugMsg("CMUXReadMsg botch 1\n");
            err = EIO; break;
        }
        dataBuf.buf += len;
        dataBuf.maxlen -= len;
        totalLen += len;
        DebugMsg("CMUXReadMsg ctllen %d datalen %d\n", ctlBuf.len, len);
        if (ctlBuf.len == 0) {
            /* end of data */
            if( (len == 0) && (totalLen == 0) ) {
                /* Bulk Data abort from previous call */
                if( ignoreAborts ) continue;
                err = EPIPE;
            }
            break;
        }
        if (dataBuf.maxlen >= MAX←MSG←RECV←BYTES) {
            /* guaranteed there's enough room for the next message */
            continue;
        }
        clientBufPtr = (*allocaterFunc)(
                totalLen, clientBufLen, allocaterClientData );
        if( clientBufPtr != NIL ) {
            dataBuf.buf = ((char *)(clientBufPtr));
            dataBuf.maxlen = clientBufLen;
            clientBufLen = 2*clientBufLen;
            continue;
        }
        /* Blow off the rest */
        for(;;) {
            flags = 0;
            dataBuf.len = ctlBuf.len = 0;
            ret = GETMSG(d, &ctlBuf, &dataBuf, &flags);
            if( (ret < 0) || (ctlBuf.len == 0) ) break;
        }
        err = ENOMEM;
        break;
    }

    if( timeoutMsec != XR←WAIT←FOREVER←MSEC ) {
        (void) XR←SetGetTimeout(d, XR←WAIT←FOREVER←MSEC);
    }

    if (err != 0) {
        (void) (*allocaterFunc)(0, 0, allocaterClientData); /* free */
        XR←SetErrno(err);
        return(-err);
    }
    return(totalLen);
}





int
XR←CMUXWriteMsg (d, hdr, hdrLen, body, bodyLen)
    XR←CMUXDescriptor d;
    XR←Pointer hdr;
    int hdrLen;
    XR←Pointer body;
    int bodyLen;
{
    struct strbuf s1;
    struct strbuf s2;
    int err;

    s1.buf = ((char *)(hdr));
    s1.len = hdrLen;
    s1.maxlen = hdrLen;

    s2.buf = ((char *)(body));
    s2.len = bodyLen;
    s2.maxlen = bodyLen;

    if( PUTMSG (d, &s1, &s2, 0) == 0 ) return(0);
    if( (err = XR←GetErrno()) != ERANGE ) {
        DebugMsg("CMUXWriteMsg PutMsg 1 failed %d\n", err);
        return(-err);
    }

    s2.len = -1;
    if( PUTMSG(d, &s1, &s2, 0) != 0 ) {
        err = XR←GetErrno();
        DebugMsg("CMUXWriteMsg PutMsg 2 failed %d\n", err);
        return(-err);
    }
    s1.len = MAX←MSG←SEND←BYTES;
    s1.buf = ((char *)(body));
    while (bodyLen > 0) {
        if( bodyLen <= (2*MAX←MSG←SEND←BYTES) ) {
            if( bodyLen > MAX←MSG←SEND←BYTES ) {
                s1.len = MAX←MSG←SEND←BYTES;
                s2.buf = &(s1.buf[MAX←MSG←SEND←BYTES]);
                s2.len = bodyLen - MAX←MSG←SEND←BYTES;
                bodyLen -= s2.len;
            } else {
                s1.len = bodyLen;
                s2.len = 0;
            }
        }
        if( PUTMSG(d, &s1, &s2, 0) != 0 ) {
            err = XR←GetErrno();
            DebugMsg("CMUXWriteMsg PutMsg 3 failed %d\n", err);
            return(-err);
        }
        bodyLen -= MAX←MSG←SEND←BYTES;
        s1.buf += MAX←MSG←SEND←BYTES;
    }
    return(0);
}


static int
XR←CMUXUnblockStream(d)
    XR←CMUXDescriptor d;
{
    int junkBuf[1];
    struct strioctl si;

    si.ic←cmd = CMUX←UNBLOCK←STREAM;
    si.ic←dp = (char *)(&junkBuf[0]);
    si.ic←len = 0;
    si.ic←timout = -1;
    return( XR←IOCtl(d, I←STR, &si) );
}


int
XR←CMUXBDTRead(d, buf, nBytes)
    XR←CMUXDescriptor d;
    XR←Pointer buf;
    unsigned nBytes;
{
    int ans;

    if( (ans = XR←Read(d, buf, nBytes)) < 0 )
        ans = -XR←GetErrno();
    return ans;
}


int
XR←CMUXBDTWrite(d, buf, nBytes)
    XR←CMUXDescriptor d;
    XR←Pointer buf;
    unsigned nBytes;
{
    int ans;

    if( (ans = XR←Write(d, buf, nBytes)) < 0 )
        ans = -XR←GetErrno();
    return ans;
}


int
XR←CMUXFinishBDT (d, source, sendAbort)
    XR←CMUXDescriptor d;
    bool source;
    bool sendAbort;
{
    char junkBuf[128];
    struct strbuf s1;
    struct strbuf s2;


    if( source ) {
        if( sendAbort ) {
            s1.buf = junkBuf;
            s1.len = 0;
            s1.maxlen = 0;
            s2.buf = junkBuf;
            s2.len = 0;
            s2.maxlen = 0;
            if( XR←PutMsg(d, &s1, &s2, 0) != -1 ) {
                while( XR←Read(d, junkBuf, (sizeof junkBuf)) > 0 )
                    continue;
            } else if( XR←GetErrno() == ERANGE ) {
                (void)XR←CMUXUnblockStream(d);
                /* Since the other side has already done an ABORT,
                 * just do an end-of-bulk indication */
                (void)XR←Write(d, junkBuf, 0);
            }
        } else /* !sendAbort */ {
            if( (XR←Write(d, junkBuf, 0) == -1)
                    &&  (XR←GetErrno() == ERANGE) ) {
                (void)XR←CMUXUnblockStream(d);
                (void)XR←Write(d, junkBuf, 0);
            }
        }
    } else /* !source */ {
        if( sendAbort ) {
            (void)XR←CMUXWriteMsg(d, junkBuf, 0, junkBuf, 0);
              while( XR←Read(d, junkBuf, (sizeof junkBuf)) > 0 )
                  continue;
        } else /* !sendAbort */ {
              /* nothing */
        }
    }
    return 0;
}


int
XR←CMUXServerAdd(d, pgm, loVersion, hiVersion)
    XR←CMUXDescriptor d;
    unsigned pgm;
    unsigned loVersion, hiVersion;
{
    struct {
        unsigned buf←pgm;
        unsigned short buf←loVersion;
        unsigned short buf←hiVersion;
    } theBuf;
    struct strioctl si;

    si.ic←cmd = CMUX←SERVER←ADD;
    LongToBE32(pgm, &(theBuf.buf←pgm) );
    ShortToBE16( ((short)(loVersion)), &(theBuf.buf←loVersion) );
    ShortToBE16( ((short)(hiVersion)), &(theBuf.buf←hiVersion) );
    si.ic←dp = (char *)(&theBuf);
    si.ic←len = (sizeof theBuf);
    si.ic←timout = -1;
    return( XR←IOCtl(d, I←STR, &si) );
}


int
XR←CMUXServerQuit(d)
    XR←CMUXDescriptor d;
{
    int junkBuf[1];
    struct strioctl si;

    si.ic←cmd = CMUX←SERVER←QUIT;
    si.ic←dp = (char *)(&junkBuf[0]);
    si.ic←len = 0;
    si.ic←timout = -1;
    return( XR←IOCtl(d, I←STR, &si) );
}



/*
 * DEBUGGING STUFF
 */

char PARC←CHS←XNSAddr[] = {
	0x00, 0x00, 0x00, 0x59,
	0x00, 0x00, 0xaa, 0x00, 0x7f, 0xac,
	0x00, 0x05
};

int
DebugCMUXPutXNSAddr(a)
    unsigned char *a;
{
    XR←ConsoleMsg( "%x.%x.%x.%x:", a[0], a[1], a[2], a[3]);
    XR←ConsoleMsg( "%x.%x.%x.", a[4], a[5], a[6]);
    XR←ConsoleMsg( "%x.%x.%x:", a[7], a[8], a[9]);
    XR←ConsoleMsg( "%x.%x\n", a[10], a[11]);
}


void
DebugCMUXFillCallHdr(hdr, pgmNum, pgmVers, procNum)
    char *hdr;
    int pgmNum, pgmVers, procNum;
{
    /* msgtype = call(0) */
	hdr[0] = 0;
	hdr[1] = 0;
    /* transactionID = 0 */
	hdr[2] = 0;
	hdr[3] = 0;
    /* program number */
	hdr[4] = (pgmNum >> 24) & 0xFF;
	hdr[5] = (pgmNum >> 16) & 0xFF;
	hdr[6] = (pgmNum >> 8) & 0xFF;
	hdr[7] = (pgmNum & 0xFF);
    /* program version */
	hdr[8] = (pgmVers >> 8) & 0xFF;
	hdr[9] = pgmVers & 0xFF;
    /* proc number */
	hdr[10] = (procNum >> 8) & 0xFF;
	hdr[11] = procNum & 0xFF;
}


XR←Pointer
DebugCMUXMkBuffer(nBytes)
    unsigned nBytes;
{
    unsigned *p = (unsigned *)XR←malloc(nBytes+sizeof(unsigned));
    p[0] = nBytes;
    return ((XR←Pointer)(p));
}


XR←Pointer
DebugCMUXAllocater(startIndex, nBytes, clientData)
    unsigned startIndex;
    unsigned nBytes;
    XR←Pointer clientData;
{
    unsigned len;

    if( clientData == NIL ) return NIL;
    len = * ((unsigned *)(clientData));
    if( len < ( sizeof(unsigned) + startIndex + nBytes ) ) return NIL;
    return ( clientData + sizeof(unsigned) + startIndex );
}

int debugCMUXSavedErrno = 0;

static int WRAP(n) { debugCMUXSavedErrno = XR←GetErrno(); return n; }

int
DebugCMUXCreate()
{
    return WRAP( XR←CMUXCreate() );
}

void
DebugCMUXDestroy(d)
    int d;
{
    (void) WRAP( (XR←CMUXDestroy(d), 0) );
}

int
DebugCMUXConnectXNSAddr(d, addr)
    int d;
    int *addr;
{
    return WRAP( XR←CMUXConnect(d, XR←CMUX←ADDR←TYPE←XNS←ADDR, addr, 12) );
}

int
DebugCMUXConnectXNSName(d, name)
    int d;
    char *name;
{
    return WRAP( XR←CMUXConnect(d, XR←CMUX←ADDR←TYPE←XNS←NAME,
            name, 1+strlen(name)) );
}

int
DebugCMUXRetrieveAddresses(d, replyBuf)
    int d;
    XR←Pointer replyBuf;
{
    int hdr[3];
    int junk = 0;
    int ans;

    DebugCMUXFillCallHdr( &(hdr[0]),
            /*pgmNum=*/ 2, /*pgmVers=*/ 3, /*procNum=*/ 0 );
    XR←ConsoleMsg("Sending call ... ");
    ans = WRAP( XR←CMUXWriteMsg(d, &(hdr[0]), (sizeof hdr), &junk, 0) );
    if( ans < 0 ) {
        DebugMsg("CMUXWriteMsg ans %d errno %d\n",
                ans, debugCMUXSavedErrno);
        return (-1);
    }
    DebugMsg("reading reply ... ");
    ans = WRAP( XR←CMUXReadMsg(d, TRUE, DebugCMUXAllocater, replyBuf) );
    if( ans < 0 ) {
        DebugMsg("CMUXReadMsg ans %d errno %d\n",
                ans, debugCMUXSavedErrno);
        return (-1);
    }
    DebugMsg( "got %d bytes\n", ans );
    return ans;
}