ATP.c

/*****************************************************************
 
    Program:    < ATP Demo >
    File:       < ATP.c >
    
    Written by  Scott Kuechle
    of <Apple Macintosh Developer Technical Support>
    
    10/92 SRK created
    8/94 SRK Modified to use a queue of parameter
             blocks.
 
    Copyright © 1992, 1994 Apple Computer, Inc.
    All rights reserved.
    
*****************************************************************/
 
/*****************************************************************/
/*  I N C L U D E S
/*****************************************************************/
 
#include    "ATP Demo.h"
#include    "ATP Demo.protos"
 
/********************************************************************
/*  G L O B A L   V A R I A B L E   D E C L A R A T I O N S
/********************************************************************/
 
 
char                gOurATPSocket;
Str255              gDataSizeStr;
Boolean             gDoContinuous;
AddrBlock           gTargetAddress;
 
myATPParamBlock     gOurPB[maxQElements];
ourRespBuf          gRespBuf[maxQElements];
BDSType             gBDS[maxQElements];
ourReqData          gOurReqData[maxQElements];
 
QHdr                gAvailQueue;
QHdr                gDoneQueue;
QHdr                gRequestQueue;
 
 
/*****************************************************************/
/*
/* E X T E R N A L S
/*
/*****************************************************************/
 
extern void     ShowError(short index);
extern void     Exit(short message);
extern Str255   gZoneString, gObjStr, gTypeStr;
extern void     registerMyName(char socket);
extern DialogPtr myDialog;
extern void     removeMyName();
extern void     FatalError(error);
extern void     HiliteSendReqButton (short mode);
extern void     PreCompletion();
extern Boolean  gReqClockTime,gSingleRequest,gStopRequests;
extern Handle   gTestDataHdl;
extern long     gTestDataSize;
 
 
 
 
#pragma segment atp
// *****************************************************************
// *    GetASocket
// *
// *    Opens a socket for us to receive requests - we will accept
// *    requests from any machine.
// *
// *****************************************************************
Boolean GetASocket(char *socket)
{
ATPParamBlock atp;
 
    *socket = 0;
    
    atp.ATP.ioCompletion = nil;
        /* dynamically allocate us a socket */
    atp.ATP.atpSocket = 0;
    
        /* accept requests from anyone */
    atp.ATP.addrBlock.aNet = 0;
    atp.ATP.addrBlock.aNode = 0;
    atp.ATP.addrBlock.aSocket = 0;
    
    if (POpenATPSkt(&atp,false) == noErr)
    {
        if (atp.ATP.ioResult == noErr)
        {
            *socket = atp.ATP.atpSocket;
            return true;
        }
    }
    
    return false;
}
 
// *****************************************************************
// *    InitQueues
// *
// *    initialize our "Available" , "Done" and "Read" queues
// *****************************************************************
 
void InitQueues()
{
short i;
long myA5;
 
    gAvailQueue.qHead   = NULL;
    gAvailQueue.qTail   = NULL;
 
    gDoneQueue.qHead    = NULL;
    gDoneQueue.qTail    = NULL;
 
    gRequestQueue.qHead = NULL;
    gRequestQueue.qTail = NULL;
    
    myA5 = *(long *)CurrentA5;
    
    for (i=0; (i < maxQElements); ++i)
    {
        gOurPB[i].reqData               = (Ptr)&gOurReqData[i];
        gOurPB[i].respData              = (Ptr)&gRespBuf[i];
        gOurPB[i].bdsPtr                = (Ptr)&gBDS[i];
        gOurPB[i].myA5                  = myA5;
        gOurPB[i].u.ATP.ioCompletion    = (ProcPtr)&PreCompletion;
 
        Enqueue((QElemPtr)&gOurPB[i].u,&gAvailQueue);
    }
 
}
 
 
// *****************************************************************
// *    GetQElement
// *
// *    retrieve a queue element from the specified queue
// *****************************************************************
 
ATPPBPtr GetQElement(QHdrPtr qHdrPtr)
{
OSErr err;
QElemPtr qElemPtr;
 
    CheckDoneQueue();
 
    qElemPtr = qHdrPtr->qHead;
    if (qElemPtr != NULL)
    {
        err = Dequeue((QElemPtr)qElemPtr,qHdrPtr);
        if (err != noErr)
        {
            return NULL;
        }
        else
        {
            return (ATPPBPtr)(qElemPtr);
        }
    }
    else
        return NULL;
}
 
// *****************************************************************
// *    GetOurPBPtr
// *
// *    Returns a pointer to our custom parameter block
// *****************************************************************
 
myATPParamBlockPtr GetOurPBPtr(ATPPBPtr atpPBPtr)
{
Ptr p;
myATPParamBlockPtr myATPPBPtr;
 
    p = (Ptr)atpPBPtr;
        /* set a pointer to our parameter block,
            which is offset kOurPBNegOffset bytes
            above the standard ATP param block */
    myATPPBPtr = (myATPParamBlockPtr)(p - kOurPBNegOffset);
    
    return (myATPPBPtr);
    
}
 
 
// *****************************************************************
// *    SetOurCompletionRoutine
// *
// *    Sets the "real" completion routine for our async. calls
// *****************************************************************
void SetOurCompletionRoutine(ProcPtr procPtr,
                            ATPPBPtr atpPBPtr)
{
myATPParamBlockPtr myATPPBPtr;
 
    myATPPBPtr = GetOurPBPtr(atpPBPtr);
    myATPPBPtr->ourCompletion = procPtr;
    
}
 
// *****************************************************************
// *    SaveFunctionResultCode
// *
// *    Places the function result code in our pb structure for later
// *    reporting.
// *****************************************************************
void SaveFunctionResultCode(OSErr err,
                            ATPPBPtr atpPBPtr)
{
myATPParamBlockPtr myATPPBPtr;
 
    myATPPBPtr = GetOurPBPtr(atpPBPtr);
        /* save off the function result */
    myATPPBPtr->functionResult = err;
        /* place pb in "done" queue so we
            can report the error later */
    Enqueue((QElemPtr)atpPBPtr,&gDoneQueue);
 
}
 
// *****************************************************************
// *    doGetRequestIOComp
// *
// *    Completion routine for our get request call. We get here if
// *    another machine sends us a request with the send request call.
// *****************************************************************
pascal void doGetRequestIOComp(ATPPBPtr atpPBPtr)
{
    if (atpPBPtr->ATP.ioResult == noErr)
            /* place parameter block into the "Request" queue */
        Enqueue((QElemPtr)atpPBPtr,&gRequestQueue);
    else    /* some kind of error was returned */
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)atpPBPtr,&gDoneQueue);
}
 
// *****************************************************************
// *    doGetRequest
// *
// *    We issue an asynchronous get request call here. It will complete
// *    if a request is received from another machine.
// *****************************************************************
void doGetRequest(ATPPBPtr atpPBPtr,
                    char socket,
                    short reqLength,
                    Ptr reqPointer)
{
OSErr err;
 
    SetOurCompletionRoutine((ProcPtr)&doGetRequestIOComp,
                            atpPBPtr);
    atpPBPtr->ATP.atpSocket     = socket;
    atpPBPtr->ATP.reqLength     = reqLength;
    atpPBPtr->ATP.reqPointer    = reqPointer;
 
    err = PGetRequest(atpPBPtr,true);
    if (err != noErr)
    {
        SaveFunctionResultCode(err,
                            atpPBPtr);
    }
    
}
 
// *****************************************************************
// *    doSndRequestIOComp
// *
// *    This is the completion routine for our send request call. It
// *    will get called as soon as a response is received from the target.
// *****************************************************************
pascal void doSndRequestIOComp(ATPPBPtr atpPBPtr)
{
    if (atpPBPtr->ATP.ioResult == noErr)
    {
            /* place parameter block into the "Request" queue */
        Enqueue((QElemPtr)atpPBPtr,&gRequestQueue);
    }
    else    /* we got an error */
    {       /* place parameter block into the "Done" queue */
        Enqueue((QElemPtr)atpPBPtr,&gDoneQueue);
    }
    
}
 
// *****************************************************************
// *    doSndRequest
// *
// *    This is our send request call. We send a request to the target,
// *    as specified in the popup menu. The call completes when a 
// *    response is received back.
// *****************************************************************
void doSndRequest(ATPPBPtr      atpPBPtr,
                    AddrBlock   address,
                    char        socket,
                    Ptr         bdsPtr,
                    short       reqLength,
                    Ptr         reqPointer,
                    char        numOfBuffs)
{
OSErr err;
 
    SetOurCompletionRoutine((ProcPtr)&doSndRequestIOComp,
                            atpPBPtr);
    atpPBPtr->SREQ.atpSocket        = socket;
    
    atpPBPtr->SREQ.addrBlock.aNet   = address.aNet;
    atpPBPtr->SREQ.addrBlock.aNode  = address.aNode;
    atpPBPtr->SREQ.addrBlock.aSocket = address.aSocket;
    
    atpPBPtr->SREQ.reqLength        = reqLength;
    atpPBPtr->SREQ.reqPointer       = reqPointer;
    
    atpPBPtr->SREQ.bdsPointer       = bdsPtr;
 
    atpPBPtr->SREQ.atpFlags         = atpEOMvalue + atpSendChkvalue;    
    atpPBPtr->SREQ.timeOutVal       = kATPTimeOutVal;
    atpPBPtr->SREQ.retryCount       = kATPRetryCount;
        /* number of response datagrams that will be accepted */
    atpPBPtr->SREQ.filler           = numOfBuffs;
 
    err = PNSendRequest(atpPBPtr,true);
    if (err != noErr)
    {
        SaveFunctionResultCode(err,
                            atpPBPtr);
    }
    
}
 
 
 
// *****************************************************************
// *    SendOurResponseData
// *
// *****************************************************************
void SendOurResponseData(ATPPBPtr atpPBPtr)
{
char reqCode;
BDSPtr bdsPtr;
myATPParamBlockPtr myATPBPtr;
short nElements;
 
        reqCode = *(atpPBPtr->ATP.reqPointer);
        switch( reqCode )
        {
            case kSendTime:
                    myATPBPtr = GetOurPBPtr(atpPBPtr);
                    bdsPtr = (BDSPtr)myATPBPtr->bdsPtr;
                    GetClockTime(myATPBPtr->respData);
                    nElements = BuildBDS(myATPBPtr->respData,
                                        myATPBPtr->bdsPtr,
                                        4);
                    doSendResponse(atpPBPtr,
                                    gOurATPSocket,
                                    (Ptr)bdsPtr,
                                    nElements,
                                    nElements);
 
                break;
            case kSendData:
                    myATPBPtr = GetOurPBPtr(atpPBPtr);                  
                    bdsPtr = (BDSPtr)myATPBPtr->bdsPtr;
                    
                        /* copy "data" from our resource into our buffer */
                    HLock(gTestDataHdl);
                    BlockMove(*gTestDataHdl,
                                myATPBPtr->respData,
                                gTestDataSize);
 
                        /* fill the BDS with our "data" */
                    nElements = BuildBDS(myATPBPtr->respData,
                                        myATPBPtr->bdsPtr,
                                        gTestDataSize);
                    HUnlock(gTestDataHdl);
                    
                    doSendResponse(atpPBPtr,
                                    gOurATPSocket,
                                    (Ptr)bdsPtr,
                                    nElements,
                                    nElements);
                break;
                
        }
}
 
// *****************************************************************
// *    doSendResponseIOComp
// *
// *    completion routine for our send response call.
// *****************************************************************
pascal void doSendResponseIOComp(ATPPBPtr atpPBPtr)
{
myATPParamBlockPtr myATPPBPtr;
 
    if (atpPBPtr->ATP.ioResult == noErr)
    {
        myATPPBPtr = GetOurPBPtr(atpPBPtr);
            /* our response was sent, so issue another async. GetRequest
                so that we may receive another request */
        doGetRequest(atpPBPtr,
                    gOurATPSocket,
                    sizeof(struct ourReqData),
                    myATPPBPtr->reqData);
    }
    else
    {
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)atpPBPtr,&gDoneQueue);
 
    }
}
 
// *****************************************************************
// *    doSendResponse
// *
// *    We send a response back to the requestor in this routine.
// *****************************************************************
void doSendResponse(ATPPBPtr atpPBPtr,
                    char    socket,
                    Ptr     bdsPtr,
                    char    numOfBuffs,
                    char    bdsSize)
{
OSErr err;
 
    SetOurCompletionRoutine((ProcPtr)&doSendResponseIOComp,
                            atpPBPtr);
 
    atpPBPtr->ATP.atpSocket         = socket;
    atpPBPtr->ATP.atpFlags          = atpEOMvalue + atpSendChkvalue;
    atpPBPtr->ATP.bdsPointer        = bdsPtr;
    atpPBPtr->OTH1.u0.numOfBuffs    = numOfBuffs;       /* num. of resp. bufs being sent */
    atpPBPtr->OTH2.bdsSize          = bdsSize;
        /* note: the "addrBlock" and "transID' fields are already
            set up for us by the just completed GetRequest call */
    err = PSendResponse(atpPBPtr,true);
    if (err != noErr)
    {
        SaveFunctionResultCode(err,
                            atpPBPtr);
    }
}
 
 
 
 
// *****************************************************************
// *    closeOurSocket
// *
// *    close the socket that we used to listen for requests.
// *****************************************************************
void closeOurSocket(char socket)
{
ATPParamBlock pb;
 
    pb.ATP.atpSocket        = socket;
    
    PCloseATPSkt(&pb,false);
}
 
 
// *****************************************************************
// *    CheckDoneQueue
// *
// *    This routine looks through the "Done" queue for calls that
// *    have completed and reports any errors.
// *****************************************************************
void CheckDoneQueue()
{
QElemPtr qElemPtr;
ATPPBPtr atpPBPtr;
myATPParamBlockPtr myATPPBPtr;
OSErr err;
 
 
        /* have any calls completed? */
    if (gDoneQueue.qHead != nil)
    {
        qElemPtr = gDoneQueue.qHead;
        err = Dequeue((QElemPtr)qElemPtr,&gDoneQueue);
        if (err == noErr)
        {
            atpPBPtr = (ATPPBPtr)qElemPtr;
            
                /* first check function result - was the
                    call queued successfully by the driver? */
            myATPPBPtr = GetOurPBPtr(atpPBPtr);
            if (myATPPBPtr->functionResult != noErr)
            {
                ShowError(DrvrErr);
            }
                /* now check ioResult - were there any
                    errors on this particular call? */
            else if (atpPBPtr->ATP.ioResult != noErr)
            {
                ShowATPError(atpPBPtr);
            }
 
                /* place queue element back into the "available" queue */
            Enqueue((QElemPtr)qElemPtr,&gAvailQueue);
        }
    }
 
}
 
// *****************************************************************
// *    VerifyData
// *
// *    Verifies that the test data we received matches our own
// *****************************************************************
Boolean VerifyData(myATPParamBlockPtr myATPPbPtr)
{
BDSPtr bds;
Ptr original,received;
Boolean valid;
short i,j;
 
    valid = true;
    bds = (BDSPtr)myATPPbPtr->bdsPtr;
    HLock(gTestDataHdl);
    original = *gTestDataHdl;
    for (i = 0;
        (i < (myATPPbPtr->u.SREQ.numOfResps)) && (valid == true);
        ++i)
    {
        received = bds->buffPtr;
        for (j = 0;
            (j < bds->dataSize) && (valid == true);
            ++j)
        {
            if (*original != *received)
                valid = false;
            ++original;
            ++received;
        }
        ++bds;  /* next BDS Element */
    }
    HUnlock(gTestDataHdl);
    
    return valid;
}
 
 
// *****************************************************************
// *    CheckRequests
// *
// *****************************************************************
void CheckRequests()
{
QElemPtr qElemPtr;
ATPPBPtr atpPBPtr;
OSErr err;
short reqCode;
myATPParamBlockPtr myATPPBPtr;
Boolean valid;
short nElements;
 
 
        /* have we received any requests? or have any requests that we
            sent completed? */
    if (gRequestQueue.qHead != nil)
    {
        qElemPtr = gRequestQueue.qHead;
        err = Dequeue((QElemPtr)qElemPtr,&gRequestQueue);
        if (err == noErr)
        {
            atpPBPtr = (ATPPBPtr)qElemPtr;
            switch (atpPBPtr->ATP.csCode)
            {
                case nSendRequest:  /* request that we sent */
                case sendRequest:
 
                            /* did we request clock time or data? */
                        reqCode = *(atpPBPtr->ATP.reqPointer);
                        switch (reqCode)
                        {
                            case kSendTime:
                                    /* show the clock time */
                                ShowClockTime(atpPBPtr);
                                break;
                            case kSendData:
                                myATPPBPtr = GetOurPBPtr(atpPBPtr);
                                    /* insert your routine here to verify data */
                                valid = VerifyData(myATPPBPtr);
                                
                                    /* if "single request" mode is selected,
                                    put up a dialog to report status */
                                if (gSingleRequest == true)
                                {
                                    if (valid == true)
                                        ShowError(dataIsValid);
                                    else
                                        ShowError(dataNotValidErr);
                                }
                                else    /* "continuous" mode, so print status text in window */
                                {
                                    if (valid == true)
                                        ShowStatusString(kTestDataCorrect);
                                    else
                                        ShowStatusString(kTestDataIncorrect);
                                }
                                break;
                        }
                        
                            /* should we re-issue another request to the target? */
                        if ((gSingleRequest == false) &&
                            (gStopRequests == false))
                        {
                            myATPPBPtr = GetOurPBPtr(atpPBPtr);
                            if (gReqClockTime == true)
                            {
                                *(myATPPBPtr->reqData) = kSendTime;
                                    /* setup response BDS to hold our
                                        clock/time value */
                                nElements = BuildBDS(myATPPBPtr->respData,
                                                    myATPPBPtr->bdsPtr,
                                                    4);
                            }
                            else    /* send test "data" */
                            {
                                *(myATPPBPtr->reqData) = kSendData;
                                nElements = BuildBDS(myATPPBPtr->respData,
                                                    myATPPBPtr->bdsPtr,
                                                    gTestDataSize);
                            }
                            
                                /* clear status string first */
                            ShowStatusString(kBlankText);
 
                                /* send a request to target machine */
                            doSndRequest(atpPBPtr,
                                        gTargetAddress,
                                        gOurATPSocket,
                                        myATPPBPtr->bdsPtr,
                                        2,
                                        myATPPBPtr->reqData,
                                        nElements);
                        }
                        else
                        {
                                /* place parameter block back into the "Avail" queue */
                            Enqueue((QElemPtr)atpPBPtr,&gAvailQueue);
                        }
                        
                    break;
                    
                case getRequest:    /* request that we received */
                
                        /* send back either our machines's clock time
                            or test "data" */
                        SendOurResponseData(atpPBPtr);
                    break;
            }
 
        }
    }
 
}
 
// *****************************************************************
// *    ATPLoop
// *
// *****************************************************************
void ATPLoop()
{
 
    CheckDoneQueue();
    CheckRequests();
 
}
 
 
// *****************************************************************
// *    SendReqToTarget
// *
// *    issues a request to the selected target machine. The target is
// *    selected using the popup menus.
// *****************************************************************
Boolean SendReqToTarget()
{
    MPPParamBlock pbLKP;
    Ptr ntePtr, buffer;
    EntityName abEntity;
    AddrBlock address;
    ATPPBPtr atpPBPtr;
    myATPParamBlockPtr myATPPBPtr;
    Boolean reqSent;
    short nElements;
 
 
        reqSent = false;
        
        ntePtr = nil;
        buffer = nil;
        
        ntePtr = NewPtr(sizeof(NamesTableEntry));
        if (ntePtr == nil)
            goto Done;
 
        buffer = NewPtr(100);
        if (buffer == nil)
            goto Done;
 
        NBPSetEntity(ntePtr, gObjStr, gTypeStr, gZoneString);
        pbLKP.NBP.interval = 3;
        pbLKP.NBP.count = 3;
        pbLKP.NBP.NBPPtrs.entityPtr = ntePtr;
        pbLKP.NBP.parm.Lookup.retBuffSize = 100;
        pbLKP.NBP.parm.Lookup.retBuffPtr = buffer;
        pbLKP.NBP.parm.Lookup.maxToGet = 1;
 
            /* first let's try and locate the selected machine on the network */
        if (PLookupName(&pbLKP, false) == noErr)
        {       /* did we find it? */
            if ( pbLKP.NBP.parm.Lookup.numGotten > 0)
            {       /* go ahead and send a request to the target */
                if (NBPExtract(buffer, pbLKP.NBP.parm.Lookup.numGotten, 1, &abEntity, &address) == noErr)
                {
                        /* save the target address */
                    gTargetAddress = address;
                    
                    atpPBPtr = GetQElement(&gAvailQueue);
                    if (atpPBPtr != NULL)
                    {
                        myATPPBPtr = GetOurPBPtr(atpPBPtr);
                            /* sending clock time or "data" ? */
                        if (gReqClockTime == true)
                        {
                            *(myATPPBPtr->reqData) = kSendTime;
                                /* setup response BDS to hold our
                                    clock/time value */
                            nElements = BuildBDS(myATPPBPtr->respData,
                                                myATPPBPtr->bdsPtr,
                                                4);
                        }
                        else    /* send test "data" */
                        {
                            *(myATPPBPtr->reqData) = kSendData;
                            nElements = BuildBDS(myATPPBPtr->respData,
                                                myATPPBPtr->bdsPtr,
                                                gTestDataSize);
                        }
                        
                            /* send a request to target machine */
                        doSndRequest(atpPBPtr,
                                    address,
                                    gOurATPSocket,
                                    myATPPBPtr->bdsPtr,
                                    2,
                                    myATPPBPtr->reqData,
                                    nElements);
                                    
                        reqSent = true;
                    }
                }
                        
            }
            else    /* target not found */
            {
                ShowError(noTargetErr);
                    /* re-enable send request button */
                HiliteSendReqButton (0);
            }
        }
        
        Done:
        
        if (ntePtr != nil)
            DisposPtr(ntePtr);
        if (buffer != nil)
            DisposPtr(buffer);
        
        return reqSent;
}
 
// *****************************************************************
// *    initializeATP
// *
// *    allocates parameter blocks for our atp calls, opens a socket
// *    for us to receive requests on, registers our name on the network.
// *****************************************************************
void initializeATP()
{
    InitQueues();
 
    if (GetASocket(&gOurATPSocket) == true)
        registerMyName(gOurATPSocket);
    else
        FatalError(SktErr);
 
}
 
// *****************************************************************
// *    removeATP
// *
// *    removes our nbp name from the network, closes our atp socket
// *    and de-allocates the memory we used for our parameter blocks.
// *****************************************************************
void removeATP()
{
    closeOurSocket(gOurATPSocket);
    removeMyName();
}
 
 
// *****************************************************************
// *    GetClockTime
// *
// *****************************************************************
void GetClockTime(Ptr buf)
{
    unsigned long secs;
 
        GetDateTime(&secs);
        BlockMove(&secs, buf, 4);
}
 
// *****************************************************************
// *    ShowStatusString
// *
// *****************************************************************
void ShowStatusString(StringPtr str)
{
    Rect r;
    short kind;
    Handle h;
 
 
        GetDItem(myDialog, kStatusText, &kind, &h, &r);
        SetIText(h, str);
 
}
 
// *****************************************************************
// *    ShowClockTime
// *
// *****************************************************************
void ShowClockTime(ATPPBPtr atpPBPtr)
{
    Rect r;
    short kind;
    Handle h;
    Str255 str,str2;
    BDSPtr bdsPtr;
    long dateTime;
 
 
        GetDItem(myDialog, kClockTime, &kind, &h, &r);
        GetIText(h, str2);
        
        bdsPtr = (BDSPtr)atpPBPtr->ATP.bdsPointer;
        BlockMove(bdsPtr->buffPtr, &dateTime, 4);
        IUTimeString(dateTime, true, str);
            /* this just checks to see that we're not already displaying this time already */
            /* flashing text sucks... so sayeth me */
        if (IUCompString(str,str2) != 0)
            SetIText(h, &str);
 
}
 
 
// *****************************************************************
// *    SetUpATPError
// *
// *    sets our error string for the desired error code
// *****************************************************************
void SetUpATPError(OSErr err,
                    StringPtr displayStr)
{
    switch (err)
    {
        case reqFailed:
            PStrCat("\pSend Request failed. Retry count exceeded.",(Ptr)displayStr);
          break;
          
        case tooManyReqs:
            PStrCat("\pToo many concurrent requests.",(Ptr)displayStr);
          break;
 
        case noDataArea:
            PStrCat("\pToo many outstanding ATP calls.",(Ptr)displayStr);
          break;
          
        case badATPSkt:
            PStrCat("\pBad responding socket.",(Ptr)displayStr);
          break;
 
        case noRelErr:
            PStrCat("\pNo release received.",(Ptr)displayStr);
          break;
 
        case badBuffNum:
            PStrCat("\pBad sequence number.",(Ptr)displayStr);
          break;
 
        case sktClosedErr:
            PStrCat("\pAsynchronous call aborted because socket was closed.",(Ptr)displayStr);
          break;
          
        default:
            NumToString(err,(Ptr)displayStr);
        
    }
 
}
 
// *****************************************************************
// *    ShowATPError
// *
// *    If one of our asynchronous atp calls returned an error, the
// *    parameter block gets stuffed into an error OSQueue by the completion
// *    routine. This error queue is checked periodically by this routine.
// *    If the queue is not empty, then we got an error so we display it here
// *    and do any processing of the error that we want.
// *****************************************************************
void ShowATPError(ATPPBPtr atpPBPtr)
{
    Rect r;
    short kind;
    Handle h;
    short itemHit;
    Str255 displayStr,theError;
    
 
            displayStr[0] = 0;
            
                /*  Use cscode field of the parameter block
                   to see which call this error is for*/
            switch (atpPBPtr->ATP.csCode)
            {
                case getRequest:
                        /* dont report errors if we cancel the call */
                    if (atpPBPtr->ATP.ioResult != reqAborted)
                    {
                        CopyPstr("\pGetRequest error. ",&displayStr);
                        SetUpATPError(atpPBPtr->ATP.ioResult, &displayStr);
                    }
                 break;
    
                case nSendRequest:
                    CopyPstr("\pSendRequest error. ",&displayStr);
                    SetUpATPError(atpPBPtr->ATP.ioResult, &displayStr);
                    
                    /* we got an error so stop sending requests if we are in
                        "continuous" mode */
                    if (gDoContinuous == true)
                    {
                        GetDItem(myDialog, kReqDataButton, &kind, &h, &r);
                        SetCTitle((ControlHandle)h, "\pRequest Data");
                    }
                 break;
            
                case sendResponse:
                    CopyPstr("\pSendResponse error. ",&displayStr);
                    SetUpATPError(atpPBPtr->ATP.ioResult, &displayStr);
                 break;
 
                default:
                    CopyPstr("\pError: ",&displayStr);
                    NumToString(atpPBPtr->ATP.ioResult,theError);
                    PStrCat(theError,(Ptr)displayStr);
                break;
    
            }
 
                /* if we have a message we want to show, put up a dialog
                    for the user */
            if (displayStr[0] != 0)
            {
                ParamText(displayStr,"\p","\p","\p");
                itemHit = Alert(rErrorDialog, nil);
            }
        
            /* re-enable send request button */
            HiliteSendReqButton (0);
        
}