home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 0 Macintosh 29Sep94 / Network.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  40.7 KB  |  1,305 lines  |  [TEXT/KAHL]

  1. /* Network.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <GestaltEqu.h>
  28. #include <PPCToolbox.h>
  29. #include <Script.h>
  30. #include <Errors.h>
  31. #ifdef THINK_C
  32.     #pragma options(!pack_enums)
  33. #endif
  34.  
  35. #include "Network.h"
  36. #include "Memory.h"
  37. #include "EventLoop.h"
  38. #include "Array.h"
  39.  
  40.  
  41. /* this is the AppleTalk PPCToolBox version of the Network module */
  42.  
  43.  
  44. /* since this system can only handle one outstanding requested connection at */
  45. /* a time, we might have to try several times to establish a connection.  these */
  46. /* specify how many times to try and how long to wait between each one. */
  47. #define MAXNUMTRIES (10)
  48. #define TRYDELAY (1.0) /* in seconds */
  49.  
  50. #define DEFAULTTYPE "PPCToolBox"
  51. #define DEFAULTTYPELENGTH (10)
  52.  
  53. /* size of a single buffer record */
  54. #define BUFFERSIZE (256)
  55.  
  56. typedef enum
  57.     {
  58.         eAwaitingConnection EXECUTE(= -8982),
  59.         eConnectionPending
  60.     } PortStates;
  61.  
  62. struct PortIDType
  63.     {
  64.         PPCPortRefNum                    ThePortRefnum;
  65.         PortStates                        PortState;
  66.         PPCInformPBRec                ThePPCInformPBRec;
  67.         PPCPortRec                        ThePPCPortRec;
  68.         PPCPortRec                        RemotePortName; /* temporary buffer for it */
  69.         LocationNameRec                RemoteLocationName; /* temporary buffer */
  70.     };
  71.  
  72. typedef struct BufferRec
  73.     {
  74.         struct BufferRec*            Next;
  75.         long                                    NumBytes;
  76.         char                                    Buffer[BUFFERSIZE];
  77.     } BufferRec;
  78.  
  79. typedef enum
  80.     {
  81.         eWriteIdle EXECUTE(= -4152),
  82.         eWriteInProgress,
  83.         eWriteFinished
  84.     } SessionWriteStates;
  85.  
  86. typedef enum
  87.     {
  88.         eReadIdle EXECUTE(= -28874),
  89.         eReadInProgress,
  90.         eReadFinished
  91.     } SessionReadStates;
  92.  
  93. typedef enum
  94.     {
  95.         eSessionNormal EXECUTE(= -9887),
  96.         eSessionDataArrived,
  97.         eSessionClosed
  98.     } SessionStates;
  99.  
  100. struct SessionIDType
  101.     {
  102.         PPCSessRefNum                    SessionRefnum;
  103.         PortIDType*                        Port;
  104.         SessionWriteStates        WriteState;
  105.         SessionReadStates            ReadState;
  106.         SessionStates                    OverallState;
  107.         BufferRec*                        SendHead;
  108.         BufferRec*                        SendTail;
  109.         BufferRec*                        SendInProgress; /* valid if WriteState != eWriteIdle */
  110.         BufferRec*                        ReceiveHead;
  111.         BufferRec*                        ReceiveTail;
  112.         BufferRec*                        ReceiveInProgress; /* valid if ReadState != eReadIdle */
  113.         PPCReadPBRec                    ThePPCReadPBRec;
  114.         PPCWritePBRec                    ThePPCWritePBRec;
  115.         PPCPortRec                        RemotePortName; /* probably not needed */
  116.         LocationNameRec                RemoteLocationName; /* NBP name of remote machine */
  117.     };
  118.  
  119.  
  120. /* maintains the status of this module for debugging */
  121. EXECUTE(static MyBoolean                    Initialized = False;)
  122.  
  123. /* list of network ports.  NIL means the port slot is available.  the length */
  124. /* is determined by the heap block size. */
  125. static ArrayRec*                                    PortArray;
  126.  
  127. /* list of sessions.  NIL means session slot is available... */
  128. static ArrayRec*                                    SessionArray;
  129.  
  130. /* This system allows you to make an outgoing connection without a port.  The */
  131. /* Macintosh requires all connections to be made through ports, so we provide */
  132. /* a "system port" through which all outgoing connections are made. */
  133. static PortIDType*                                AppleTalkSystemPort;
  134.  
  135. /* session scan counter.  this is used so that all of the sessions are checked */
  136. /* fairly instead of favoring ones that are near the beginning of the list */
  137. static long                                                SessionScan;
  138.  
  139.  
  140. /* utility function prototypes */
  141. static void                    ReinstallPPCInform(PortIDType* Port);
  142. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  143.                                             char TypeStr[32], char ZoneStr[32]);
  144. static void                    UpdateRead(SessionIDType* Session);
  145. static void                    UpdateWrite(SessionIDType* Session);
  146. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB);
  147. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB);
  148. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB);
  149.  
  150.  
  151. /* initialize the network interface.  the user calls this.  it is not called from */
  152. /* the standard Level 0 initialization routine because not all programs need */
  153. /* networking.  Could return eNetNoError, eNetNoMemory, eNetNetworkNotAvailable, */
  154. /* eNetCouldntInitNet, or eNetUnknownError */
  155. NetErrors                        InitializeNetwork(void)
  156.     {
  157.         OSErr                            Error;
  158.         long                            Result;
  159.         NetErrors                    ReturnValue;
  160.         unsigned long            PortIDScan;
  161.  
  162.         ERROR(Initialized,PRERR(ForceAbort,"InitializeNetwork:  already initialized"));
  163.         EXECUTE(Initialized = True;)
  164.         Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  165.         if (Error != noErr)
  166.             {
  167.                 ReturnValue = eNetNetworkNotAvailable;
  168.              FailurePoint1:
  169.                 EXECUTE(Initialized = False;)
  170.                 return ReturnValue;
  171.             }
  172.         if ((Result & gestaltPPCSupportsRealTime) == 0)
  173.             {
  174.                 /* not initialized.  try initializing */
  175.                 Error = PPCInit();
  176.                 if (Error != noErr)
  177.                     {
  178.                         ReturnValue = eNetCouldntInitNet;
  179.                         goto FailurePoint1;
  180.                     }
  181.                 Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  182.             }
  183.         /* don't know what to do in these cases */
  184.         if ((Result & gestaltPPCSupportsOutGoing) == 0)
  185.             {
  186.             }
  187.         if ((Result & gestaltPPCSupportsIncoming) == 0)
  188.             {
  189.             }
  190.         /* allocate arrays for holding session and port records */
  191.         PortArray = NewArray();
  192.         if (PortArray == NIL)
  193.             {
  194.                 ReturnValue = eNetNoMemory;
  195.              FailurePoint2:
  196.                 goto FailurePoint1;
  197.             }
  198.         SessionArray = NewArray();
  199.         if (SessionArray == NIL)
  200.             {
  201.                 ReturnValue = eNetNoMemory;
  202.              FailurePoint3:
  203.                 DisposeArray(PortArray);
  204.                 goto FailurePoint2;
  205.             }
  206.         /* initialize the session counter for NetUpdate */
  207.         SessionScan = 0;
  208.         /* trying to obtain a port for outgoing connections.  port names must be unique */
  209.         /* and since there may be several programs using this package running on the */
  210.         /* same computer, we need to search for an available port to work out of. */
  211.         for (PortIDScan = 0xffffffff; PortIDScan > 0; PortIDScan -= 1)
  212.             {
  213.                 char*                            PortNameTemp;
  214.                 int                                Scan;
  215.  
  216.                 PortNameTemp = AllocPtrCanFail(8,"SysPortName");
  217.                 if (PortNameTemp == NIL)
  218.                     {
  219.                         ReturnValue = eNetNoMemory;
  220.                         goto FailurePoint3;
  221.                     }
  222.                 for (Scan = 0; Scan < 8; Scan += 1)
  223.                     {
  224.                         PortNameTemp[Scan] = 'a' + ((PortIDScan >> (Scan * 4)) & 15);
  225.                     }
  226.                 /* try to open the port */
  227.                 ReturnValue = NetListenAtPort(PortNameTemp,&AppleTalkSystemPort,eNetAppleTalk);
  228.                 ReleasePtr(PortNameTemp);
  229.                 /* see if the port worked */
  230.                 switch (ReturnValue)
  231.                     {
  232.                         default:
  233.                             EXECUTE(PRERR(ForceAbort,
  234.                                 "InitializeNetwork:  bad value from NetListenAtPort"));
  235.                             break;
  236.                         case eNetNoError:
  237.                             goto SystemPortAllocatedPoint;
  238.                         case eNetNoMemory:
  239.                             /* value of ReturnValue stays the same */
  240.                             /* ReturnValue = eNetNoMemory; */
  241.                          FailurePoint4:
  242.                             DisposeArray(SessionArray);
  243.                             goto FailurePoint3;
  244.                         case eNetPortInUse:
  245.                             break; /* loop again */
  246.                         case eNetBadPortString:
  247.                             EXECUTE(PRERR(ForceAbort,"InitializeNetwork:  bad port string"));
  248.                             break;
  249.                         case eNetUnknownError:
  250.                             /* value of ReturnValue stays the same */
  251.                             /* ReturnValue = eNetUnknownError; */
  252.                             goto FailurePoint4;
  253.                             break;
  254.                     }
  255.             }
  256.         /* fall through:  couldn't allocate any system ports */
  257.         ReturnValue = eNetUnknownError;
  258.         goto FailurePoint4;
  259.         /* jump here if all is good */
  260.      SystemPortAllocatedPoint:
  261.         return eNetNoError;
  262.     }
  263.  
  264.  
  265. /* discard any pending data, close any open connections, and clean internal data */
  266. void                                ShutdownNetwork(void)
  267.     {
  268.         long                            Scan;
  269.         long                            Limit;
  270.  
  271.         ERROR(!Initialized,PRERR(ForceAbort,"ShutdownNetwork:  not initialized"));
  272.         /* first, close all sessions */
  273.         Limit = ArrayGetLength(SessionArray);
  274.         for (Scan = 0; Scan < Limit; Scan += 1)
  275.             {
  276.                 SessionIDType*        Session;
  277.  
  278.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  279.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  session still open"));
  280.                 NetCloseSession(Session);
  281.             }
  282.         /* now close all open ports, including the system port */
  283.         NetTerminatePortAndSessions(AppleTalkSystemPort); /* technically not needed */
  284.         Limit = ArrayGetLength(PortArray);
  285.         for (Scan = 0; Scan < Limit; Scan += 1)
  286.             {
  287.                 PortIDType*                Port;
  288.  
  289.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  290.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  found a port that's still open"));
  291.                 NetTerminatePortAndSessions(Port);
  292.             }
  293.         /* now release memory occupied by arrays */
  294.         DisposeArray(PortArray);
  295.         DisposeArray(SessionArray);
  296.         EXECUTE(Initialized = False;)
  297.     }
  298.  
  299.  
  300. /* Network update routine.  It handles periodic update tasks and should be called */
  301. /* frequently (preferably in the main event loop next to GetAnEvent). */
  302. NetEvents                        NetUpdate(SessionIDType** SessionNumber)
  303.     {
  304.         OSErr                            Error;
  305.         long                            Scan;
  306.         long                            Limit;
  307.         long                            OldSessionScan;
  308.         NetEvents                    ReturnValue;
  309.  
  310.         ERROR(SessionNumber == NIL,PRERR(ForceAbort,"NetUpdate:  SessionNumber == NIL"));
  311.         ERROR(!Initialized,PRERR(ForceAbort,"NetUpdate:  not initialized"));
  312.         /* look for PPCInforms that completed */
  313.         Limit = ArrayGetLength(PortArray);
  314.         for (Scan = 0; Scan < Limit; Scan += 1)
  315.             {
  316.                 PortIDType*                Port;
  317.  
  318.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  319.                 if (Port->PortState == eConnectionPending)
  320.                     {
  321.                         SessionIDType*        Session;
  322.  
  323.                         /* return an event indicating the new session and reset the PPCInform */
  324.                         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"Session");
  325.                         if ((Session == NIL) || (Port == AppleTalkSystemPort))
  326.                             {
  327.                                 PPCEndPBRec                ThePPCEndPBRec;
  328.  
  329.                                 /* if we are out of memory, then kill the session */
  330.                                 /* we also do this if the connection was established on the */
  331.                                 /* system port */
  332.                              NewSessionFailurePoint1:
  333.                                 ThePPCEndPBRec.sessRefNum = Port->ThePPCInformPBRec.sessRefNum;
  334.                                 Error = PPCEnd(&ThePPCEndPBRec,False/*sync*/);
  335.                                 ERROR(Error != noErr,PRERR(AllowResume,"NetUpdate:  PPCEnd != noErr"));
  336.                                 ReinstallPPCInform(Port); /* this resets PortState */
  337.                                 goto NextPortPoint;
  338.                             }
  339.                         if (!ArrayAppendElement(SessionArray,Session))
  340.                             {
  341.                                 goto NewSessionFailurePoint1;
  342.                             }
  343.                         /* initialize all of the fields of the session record */
  344.                         Session->SessionRefnum = Port->ThePPCInformPBRec.sessRefNum;
  345.                         Session->Port = Port;
  346.                         Session->WriteState = eWriteIdle;
  347.                         Session->ReadState = eReadIdle;
  348.                         Session->OverallState = eSessionNormal;
  349.                         Session->SendHead = NIL;
  350.                         Session->SendTail = NIL;
  351.                         Session->SendInProgress = NIL;
  352.                         Session->ReceiveHead = NIL;
  353.                         Session->ReceiveTail = NIL;
  354.                         Session->ReceiveInProgress = NIL;
  355.                         /* initiate the first PPC read operation */
  356.                         UpdateRead(Session);
  357.                         /* copy over the port and location names of the remote session */
  358.                         Session->RemotePortName = Port->RemotePortName;
  359.                         Session->RemoteLocationName = Port->RemoteLocationName;
  360.                         ReinstallPPCInform(Port); /* this resets PortState */
  361.                         /* return session number for caller */
  362.                         *SessionNumber = Session;
  363.                         return eNetEventNewSession;
  364.                     }
  365.                 /* jump here after killing a session that we couldn't handle so that */
  366.                 /* we can check the next port. */
  367.              NextPortPoint:
  368.                 ;
  369.             }
  370.         /* now do session scans */
  371.         ReturnValue = eNetEventNone;
  372.         Limit = ArrayGetLength(SessionArray);
  373.         if (Limit > 0)
  374.             {
  375.                 OldSessionScan = SessionScan;
  376.                 if (OldSessionScan > Limit - 1)
  377.                     {
  378.                         /* this prevents us from getting stuck in an infinite loop if someone */
  379.                         /* removed an array element and left SessionScan beyond the end of the array */
  380.                         OldSessionScan = 0;
  381.                     }
  382.                 do
  383.                     {
  384.                         SessionIDType*            Session;
  385.  
  386.                         /* we increment first so that we don't dwell on hot sessions */
  387.                         SessionScan += 1;
  388.                         if (SessionScan >= Limit)
  389.                             {
  390.                                 SessionScan = 0;
  391.                             }
  392.                         Session = (SessionIDType*)ArrayGetElement(SessionArray,SessionScan);
  393.                         /* check to see if port is closed */
  394.                         if (Session->OverallState == eSessionClosed)
  395.                             {
  396.                                 *SessionNumber = Session;
  397.                                 return eNetEventSessionClosed;
  398.                             }
  399.                         /* check to see if write needs to be retriggered */
  400.                         if (Session->WriteState != eWriteInProgress)
  401.                             {
  402.                                 if (Session->WriteState == eWriteFinished)
  403.                                     {
  404.                                         ReturnValue = eNetEventInternal;
  405.                                     }
  406.                                 UpdateWrite(Session);
  407.                             }
  408.                         /* check to see if read needs to be retriggered */
  409.                         if (Session->ReadState != eReadInProgress)
  410.                             {
  411.                                 if (Session->ReadState == eReadFinished)
  412.                                     {
  413.                                         ReturnValue = eNetEventInternal;
  414.                                     }
  415.                                 UpdateRead(Session);
  416.                             }
  417.                         /* check to see if there is new data */
  418.                         if (Session->OverallState == eSessionDataArrived)
  419.                             {
  420.                                 Session->OverallState = eSessionNormal;
  421.                                 *SessionNumber = Session;
  422.                                 return eNetEventDataIncoming;
  423.                             }
  424.                     } while (SessionScan != OldSessionScan);
  425.             }
  426.         return ReturnValue;
  427.     }
  428.  
  429.  
  430. /* local utility routine for installing an asynchronous PPC callback */
  431. static void                    ReinstallPPCInform(PortIDType* Port)
  432.     {
  433.         OSErr                            Error;
  434.  
  435.         Port->ThePPCInformPBRec.ioCompletion = (ProcPtr)&MyPPCInformCompletionRoutine;
  436.         Port->ThePPCInformPBRec.portRefNum = Port->ThePortRefnum;
  437.         Port->ThePPCInformPBRec.autoAccept = True;
  438.         /* provide a place for identifying the remote port */
  439.         Port->ThePPCInformPBRec.portName = &(Port->RemotePortName);
  440.         /* provide a place for identifying the remote machine */
  441.         Port->ThePPCInformPBRec.locationName = &(Port->RemoteLocationName);
  442.         /* we don't support user names */
  443.         Port->ThePPCInformPBRec.userName = NIL;
  444.         Port->PortState = eAwaitingConnection; /* BEFORE the call */
  445.         Error = PPCInform(&(Port->ThePPCInformPBRec),True/*async*/);
  446.         ERROR(Error != noErr,PRERR(AllowResume,"ReinstallPPCInform:  PPCInform != noErr"));
  447.     }
  448.  
  449.  
  450. /* Listen at the specified port.  Returns the reference number of the port being */
  451. /* listened at.  PortString is a non-null-terminated heap block containing the string */
  452. /* identifying the port.  The port string format depends on the underlying network */
  453. /* scheme.  For instance, TCP/IP would be an integer between 128 and 9999. */
  454. /* for AppleTalk (NBP), it is a valid NBP name.  Could return eNetNoError, */
  455. /* eNetNoMemory, eNetPortInUse, eNetBadPortString, eNetUnknownError, or */
  456. /* eNetProtocolNotSupported. */
  457. NetErrors                        NetListenAtPort(char* PortString, PortIDType** PortOut,
  458.                                             NetworkTypes WhichNetwork)
  459.     {
  460.         PPCOpenPBRec            ThePPCOpenPBRec;
  461.         long                            PortStringLength;
  462.         PortIDType*                Port;
  463.         OSErr                            Error;
  464.         NetErrors                    ReturnValue;
  465.  
  466.         ERROR(!Initialized,PRERR(ForceAbort,"NetListenAtPort:  not initialized"));
  467.         CheckPtrExistence(PortString);
  468.         ERROR(PortOut == NIL,PRERR(ForceAbort,"NetListenAtPort:  PortOut is NIL"));
  469.         /* verify network connection requested */
  470.         switch (WhichNetwork)
  471.             {
  472.                 case eNetDefault:
  473.                 case eNetAppleTalk:
  474.                     break;
  475.                 case eNetTCP:
  476.                     return eNetProtocolNotSupported;
  477.                 default:
  478.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  479.                     break;
  480.             }
  481.         /* make a place for it */
  482.         Port = (PortIDType*)AllocPtrCanFail(sizeof(PortIDType),"PortIDType");
  483.         if (Port == NIL)
  484.             {
  485.                 ReturnValue = eNetNoMemory;
  486.              FailurePoint1:
  487.                 return ReturnValue;
  488.             }
  489.         if (!ArrayAppendElement(PortArray,Port))
  490.             {
  491.                 ReturnValue = eNetNoMemory;
  492.              FailurePoint2:
  493.                 ReleasePtr((char*)Port);
  494.                 goto FailurePoint1;
  495.             }
  496.         /* initialize the port record */
  497.         Port->ThePPCPortRec.nameScript = smRoman;
  498.         PortStringLength = PtrSize(PortString);
  499.         if (PortStringLength > 31)
  500.             {
  501.                 ReturnValue = eNetBadPortString;
  502.              FailurePoint3:
  503.                 ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  504.                 goto FailurePoint2;
  505.             }
  506.         CopyData(PortString,(char*)&(Port->ThePPCPortRec.name[1]),PortStringLength);
  507.         Port->ThePPCPortRec.name[0] = PortStringLength; /* stupid pascal strings */
  508.         Port->ThePPCPortRec.portKindSelector = ppcByString;
  509.         CopyData(DEFAULTTYPE,(char*)&(Port->ThePPCPortRec.u.portTypeStr[1]),
  510.             DEFAULTTYPELENGTH);
  511.         Port->ThePPCPortRec.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  512.         /* initialize the Openrec */
  513.         ThePPCOpenPBRec.serviceType = ppcServiceRealTime;
  514.         ThePPCOpenPBRec.resFlag = 0;
  515.         ThePPCOpenPBRec.portName = &(Port->ThePPCPortRec);
  516.         ThePPCOpenPBRec.locationName = NIL;
  517.         ThePPCOpenPBRec.networkVisible = True;
  518.         /* try to open it */
  519.         Error = PPCOpen(&ThePPCOpenPBRec,False/*synchronous*/);
  520.         switch (Error)
  521.             {
  522.                 case noErr:
  523.                     Port->ThePortRefnum = ThePPCOpenPBRec.portRefNum;
  524.                     break; /* continue on to the next phase */
  525.                 case badLocNameErr:
  526.                     ReturnValue = eNetBadPortString;
  527.                     goto FailurePoint3;
  528.                 case portNameExistsErr:
  529.                 case nbpDuplicate:
  530.                     ReturnValue = eNetPortInUse;
  531.                     goto FailurePoint3;
  532.                 default:
  533.                     ReturnValue = eNetUnknownError;
  534.                     goto FailurePoint3;
  535.             }
  536.         /* indicate that the callback is out there */
  537.         Port->PortState = eAwaitingConnection;
  538.         /* enable listen callback */
  539.         ReinstallPPCInform(Port);
  540.         /* put reference number out for caller */
  541.         *PortOut = Port;
  542.         return eNetNoError;
  543.     }
  544.  
  545.  
  546. /* Stop listening at a port and let the OS use it for someone else.  Sessions */
  547. /* established through this port are terminated. */
  548. void                                NetTerminatePortAndSessions(PortIDType* Port)
  549.     {
  550.         OSErr                            Error;
  551.         PPCClosePBRec            TheCloseRec;
  552.         long                            Scan;
  553.         long                            Limit;
  554.  
  555.         ERROR(!Initialized,PRERR(ForceAbort,
  556.             "NetTerminatePortAndSessions:  not initialized"));
  557.         CheckPtrExistence(Port);
  558.         /* close any sessions registered through this port */
  559.         Limit = ArrayGetLength(SessionArray);
  560.         Scan = 0;
  561.         while (Scan < Limit)
  562.             {
  563.                 SessionIDType*            Session;
  564.  
  565.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  566.                 if (Session->Port == Port)
  567.                     {
  568.                         /* clean up our local data structures */
  569.                         NetCloseSession(Session);
  570.                         /* decrement limit, since we just dropped current element from array */
  571.                         Limit -= 1;
  572.                     }
  573.                  else
  574.                     {
  575.                         /* only increment this if we didn't delete the element */
  576.                         Scan += 1;
  577.                     }
  578.             }
  579.         /* close the port itself */
  580.         TheCloseRec.portRefNum = Port->ThePortRefnum;
  581.         Error = PPCClose(&TheCloseRec,False/*synchronous*/);
  582.         ERROR(Error != noErr,PRERR(AllowResume,
  583.             "NetTerminatePortAndSessions:  return from PPCClose != noErr"));
  584.         ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  585.         ReleasePtr((char*)Port);
  586.     }
  587.  
  588.  
  589. /* Open a session to another (or the same) machine.  Returns a session number in */
  590. /* *SessionOut.  PortString is the remote port to connect to, as described above, */
  591. /* and MachineString is a machine string determined by the network stack.  For */
  592. /* instance, TCP/IP would support standard a.b.c.d or machine.zone.domain format. */
  593. /* The Macintosh uses machine:type@zone.  Could return eNetNoError, eNetNoMemory, */
  594. /* eNetBadPortString, eNetBadMachineString, eNetMachineUnknown, eNetUnknownError, */
  595. /* eNetConnectRefused, or eNetProtocolNotSupported. */
  596. NetErrors                        NetOpenSession(char* PortString, char* MachineString,
  597.                                             SessionIDType** SessionOut, NetworkTypes WhichNetwork)
  598.     {
  599.         PPCStartPBRec            ThePPCStartPBRec;
  600.         OSErr                            Error;
  601.         long                            PortNameLength;
  602.         SessionIDType*        Session;
  603.         NetErrors                    ReturnValue;
  604.         long                            TryCounter;
  605.         unsigned char            UserNamePlace[32];
  606.  
  607.         ERROR(!Initialized,PRERR(ForceAbort,"NetOpenSession:  not initialized"));
  608.         CheckPtrExistence(PortString);
  609.         CheckPtrExistence(MachineString);
  610.         ERROR(SessionOut == NIL,PRERR(ForceAbort,"NetOpenSession:  SessionOut == NIL"));
  611.         /* verify network connection requested */
  612.         switch (WhichNetwork)
  613.             {
  614.                 case eNetDefault:
  615.                 case eNetAppleTalk:
  616.                     break;
  617.                 case eNetTCP:
  618.                     return eNetProtocolNotSupported;
  619.                 default:
  620.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  621.                     break;
  622.             }
  623.         /* allocate a place for it */
  624.         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"SessionIDType");
  625.         if (Session == NIL)
  626.             {
  627.                 ReturnValue = eNetNoMemory;
  628.              FailurePoint1:
  629.                 return ReturnValue;
  630.             }
  631.         if (!ArrayAppendElement(SessionArray,Session))
  632.             {
  633.                 ReturnValue = eNetNoMemory;
  634.              FailurePoint2:
  635.                 ReleasePtr((char*)Session);
  636.                 goto FailurePoint1;
  637.             }
  638.         /* initialize the ppc record */
  639.         EXECUTE(ThePPCStartPBRec.ioCompletion = (ProcPtr)0x81818181;)
  640.         ThePPCStartPBRec.portRefNum = AppleTalkSystemPort->ThePortRefnum;
  641.         ThePPCStartPBRec.serviceType = ppcServiceRealTime;
  642.         ThePPCStartPBRec.resFlag = 0;
  643.         /* see if this connection is for this machine or another */
  644.         PortNameLength = PtrSize(PortString);
  645.         if (PortNameLength > 31)
  646.             {
  647.                 ReturnValue = eNetBadPortString;
  648.              FailurePoint3:
  649.                 ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  650.                 goto FailurePoint2;
  651.             }
  652.         CopyData(PortString,(char*)&(Session->RemotePortName.name[1]),PortNameLength);
  653.         Session->RemotePortName.name[0] = PortNameLength;
  654.         Session->RemotePortName.nameScript = smRoman;
  655.         Session->RemotePortName.portKindSelector = ppcByString;
  656.         CopyData(DEFAULTTYPE,(char*)&(Session->RemotePortName.u.portTypeStr[1]),
  657.             DEFAULTTYPELENGTH);
  658.         Session->RemotePortName.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  659.         ThePPCStartPBRec.portName = &(Session->RemotePortName);
  660.         if ((PtrSize(MachineString) != 9) && (PtrSize(MachineString) != 0))
  661.             {
  662.                 char                            MyNBPObjectStr[32];
  663.                 char                            MyNBPTypeStr[32];
  664.                 char                            MyNBPZoneStr[32];
  665.  
  666.                 /* remote machine specified */
  667.              RemoteName:
  668.                 if (!DecodeMachineName(MachineString,MyNBPObjectStr,MyNBPTypeStr,MyNBPZoneStr))
  669.                     {
  670.                         ReturnValue = eNetBadMachineString;
  671.                         goto FailurePoint2;
  672.                     }
  673.                 CopyData(&(MyNBPObjectStr[0]),(char*)&(Session->RemoteLocationName
  674.                     .u.nbpEntity.objStr),MyNBPObjectStr[0] + 1);
  675.                 CopyData(&(MyNBPTypeStr[0]),(char*)&(Session->RemoteLocationName
  676.                     .u.nbpEntity.typeStr),MyNBPTypeStr[0] + 1);
  677.                 CopyData(&(MyNBPZoneStr[0]),(char*)&(Session->RemoteLocationName
  678.                     .u.nbpEntity.zoneStr),MyNBPZoneStr[0] + 1);
  679.                 Session->RemoteLocationName.locationKindSelector = ppcNBPLocation;
  680.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  681.             }
  682.          else
  683.             {
  684.                 if (PtrSize(MachineString) == 9)
  685.                     {
  686.                         long                            Scan;
  687.  
  688.                         for (Scan = 0; Scan < 9; Scan += 1)
  689.                             {
  690.                                 if ("localhost"[Scan] != MachineString[Scan])
  691.                                     {
  692.                                         goto RemoteName;
  693.                                     }
  694.                             }
  695.                     }
  696.                 /* it's the local machine */
  697.                 Session->RemoteLocationName.locationKindSelector = ppcNoLocation;
  698.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  699.             }
  700.         ThePPCStartPBRec.userData = 0; /* users are not used */
  701.         if (noErr != GetDefaultUser(&(ThePPCStartPBRec.userRefNum),UserNamePlace))
  702.             {
  703.                 ThePPCStartPBRec.userRefNum = 0;
  704.             }
  705.         TryCounter = MAXNUMTRIES;
  706.      TryAgainPoint:
  707.         Error = PPCStart(&ThePPCStartPBRec,False/*sync*/);
  708.         switch (Error)
  709.             {
  710.                 default:
  711.                     ReturnValue = eNetUnknownError;
  712.                     goto FailurePoint3;
  713.                 case destPortErr:
  714.                 case noResponseErr:
  715.                 case userRejectErr:
  716.                 case localOnlyErr:
  717.                 case guestNotAllowedErr:
  718.                     ReturnValue = eNetConnectRefused;
  719.                     goto FailurePoint3;
  720.                 case noErr:
  721.                     break; /* continue */
  722.                 case noInformErr:
  723.                     TryCounter -= 1;
  724.                     /* sleep a little bit */
  725.                     {
  726.                         double            Now;
  727.  
  728.                         Now = ReadTimer();
  729.                         while (TimerDifference(ReadTimer(),Now) < TRYDELAY)
  730.                             {
  731.                                 RelinquishCPUCheckCancel();
  732.                             }
  733.                     }
  734.                     /* see if we should try again */
  735.                     if (TryCounter > 0)
  736.                         {
  737.                             goto TryAgainPoint;
  738.                         }
  739.                     ReturnValue = eNetConnectRefused;
  740.                     goto FailurePoint3;
  741.                 case portClosedErr:
  742.                 case badPortNameErr:
  743.                 case noUserRecErr:
  744.                 case noPortErr:
  745.                 case badServiceMethodErr:
  746.                 case nameTypeErr:
  747.                     EXECUTE(PRERR(ForceAbort,"NetOpenSession:  bad error code"));
  748.                     break;
  749.             }
  750.         /* initialize all of the structure's parameters */
  751.         Session->SessionRefnum = ThePPCStartPBRec.sessRefNum;
  752.         Session->Port = AppleTalkSystemPort;
  753.         Session->WriteState = eWriteIdle;
  754.         Session->ReadState = eReadIdle;
  755.         Session->OverallState = eSessionNormal;
  756.         Session->SendHead = NIL;
  757.         Session->SendTail = NIL;
  758.         Session->SendInProgress = NIL;
  759.         Session->ReceiveHead = NIL;
  760.         Session->ReceiveTail = NIL;
  761.         Session->ReceiveInProgress = NIL;
  762.         *SessionOut = Session;
  763.         return eNetNoError;
  764.     }
  765.  
  766.  
  767. /* utility routine to decode string of format <name>:<type>@<zone> */
  768. /* we allow the omission of <type> and automatically substitute DEFAULTTYPE for it */
  769. /* you can omit the zone name as well. */
  770. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  771.                                             char TypeStr[32], char ZoneStr[32])
  772.     {
  773.         long                            Limit;
  774.         long                            Scan;
  775.  
  776.         Limit = PtrSize(MachineStr);
  777.         Scan = 0;
  778.         while ((Scan < Limit) && (MachineStr[Scan] != ':') && (MachineStr[Scan] != '@'))
  779.             {
  780.                 if (Scan >= 31)
  781.                     {
  782.                         return False;
  783.                     }
  784.                 ObjStr[Scan + 1] = MachineStr[Scan];
  785.                 Scan += 1;
  786.             }
  787.         ObjStr[0] = Scan;
  788.         if ((Scan == Limit) || (MachineStr[Scan] == '@'))
  789.             {
  790.                 /* type name omitted */
  791.                 CopyData(DEFAULTTYPE,&(TypeStr[1]),DEFAULTTYPELENGTH);
  792.                 TypeStr[0] = DEFAULTTYPELENGTH;
  793.                 goto ElidedTypePoint;
  794.             }
  795.         MachineStr += Scan + 1;
  796.         Limit -= Scan + 1;
  797.         Scan = 0;
  798.         while ((Scan < Limit) && (MachineStr[Scan] != '@'))
  799.             {
  800.                 if (Scan >= 31)
  801.                     {
  802.                         return False;
  803.                     }
  804.                 TypeStr[Scan + 1] = MachineStr[Scan];
  805.                 Scan += 1;
  806.             }
  807.         TypeStr[0] = Scan;
  808.      ElidedTypePoint:
  809.         if (Scan == Limit)
  810.             {
  811.                 /* no zone name */
  812.                 ZoneStr[0] = 0;
  813.                 return True;
  814.             }
  815.         MachineStr += Scan + 1;
  816.         Limit -= Scan + 1;
  817.         Scan = 0;
  818.         while (Scan < Limit)
  819.             {
  820.                 if (Scan >= 31)
  821.                     {
  822.                         return False;
  823.                     }
  824.                 ZoneStr[Scan + 1] = MachineStr[Scan];
  825.                 Scan += 1;
  826.             }
  827.         ZoneStr[0] = Scan;
  828.         return True;
  829.     }
  830.  
  831.  
  832. /* Close a session.  Any waiting data in either direction is discarded. */
  833. void                                NetCloseSession(SessionIDType* Session)
  834.     {
  835.         OSErr                            Error;
  836.         PPCEndPBRec                EndRec;
  837.         BufferRec*                BuffScan;
  838.  
  839.         ERROR(!Initialized,PRERR(ForceAbort,"NetCloseSession:  not initialized"));
  840.         CheckPtrExistence(Session);
  841.         /* first, kill the session to complete any reads/writes in progress. */
  842.         /* this might fail if the session is already dead. */
  843.         EndRec.sessRefNum = Session->SessionRefnum;
  844.         Error = PPCEnd(&EndRec,False/*sync*/);
  845.         /* now that that's finished, we can dump the buffers */
  846.         if (Session->ReceiveInProgress != NIL)
  847.             {
  848.                 ReleasePtr((char*)(Session->ReceiveInProgress));
  849.             }
  850.         if (Session->SendInProgress != NIL)
  851.             {
  852.                 ReleasePtr((char*)(Session->SendInProgress));
  853.             }
  854.         BuffScan = Session->SendHead;
  855.         while (BuffScan != NIL)
  856.             {
  857.                 BufferRec*                BuffTemp;
  858.  
  859.                 BuffTemp = BuffScan;
  860.                 BuffScan = BuffScan->Next;
  861.                 ReleasePtr((char*)BuffTemp);
  862.             }
  863.         BuffScan = Session->ReceiveHead;
  864.         while (BuffScan != NIL)
  865.             {
  866.                 BufferRec*                BuffTemp;
  867.  
  868.                 BuffTemp = BuffScan;
  869.                 BuffScan = BuffScan->Next;
  870.                 ReleasePtr((char*)BuffTemp);
  871.             }
  872.         /* now dump the session record */
  873.         ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  874.         ReleasePtr((char*)Session);
  875.     }
  876.  
  877.  
  878. /* utility routine to submit a new read operation if one has completed */
  879. static void                    UpdateRead(SessionIDType* Session)
  880.     {
  881.         OSErr                            Error;
  882.  
  883.         CheckPtrExistence(Session);
  884.         ERROR(Session->ReadState == eReadInProgress,PRERR(ForceAbort,
  885.             "UpdateRead:  read is already in progress"));
  886.         /* handle the data that comes back from a read operation. */
  887.         if (Session->ReadState == eReadFinished)
  888.             {
  889.                 CheckPtrExistence(Session->ReceiveInProgress);
  890.                 /* check for session closed notification */
  891.                 if (Session->ThePPCReadPBRec.ioResult == sessClosedErr)
  892.                     {
  893.                         Session->OverallState = eSessionClosed;
  894.                     }
  895.                 ERROR((Session->ThePPCReadPBRec.ioResult != sessClosedErr)
  896.                     && (Session->ThePPCReadPBRec.ioResult != noErr),PRERR(AllowResume,
  897.                     "UpdateRead:  error returned from PPCRead"));
  898.                 /* get read length */
  899.                 Session->ReceiveInProgress->NumBytes = Session->ThePPCReadPBRec.actualLength;
  900.                 ERROR((Session->ReceiveInProgress->NumBytes < 0)
  901.                     || (Session->ReceiveInProgress->NumBytes > BUFFERSIZE),PRERR(ForceAbort,
  902.                     "UpdateRead:  too many bytes returned from PPCRead"));
  903.                 if (Session->ReceiveInProgress->NumBytes != 0)
  904.                     {
  905.                         /* move this buffer onto the list */
  906.                         Session->ReceiveInProgress->Next = NIL;
  907.                         if (Session->ReceiveTail != NIL)
  908.                             {
  909.                                 CheckPtrExistence(Session->ReceiveTail);
  910.                                 Session->ReceiveTail->Next = Session->ReceiveInProgress;
  911.                             }
  912.                          else
  913.                             {
  914.                                 Session->ReceiveHead = Session->ReceiveInProgress;
  915.                             }
  916.                         Session->ReceiveTail = Session->ReceiveInProgress;
  917.                     }
  918.                  else
  919.                     {
  920.                         ReleasePtr((char*)(Session->ReceiveInProgress));
  921.                     }
  922.                 /* clear buffer register */
  923.                 Session->ReceiveInProgress = NIL;
  924.                 /* indicate that data has arrived */
  925.                 if (Session->OverallState == eSessionNormal)
  926.                     {
  927.                         /* don't change this if it contains eSessionClosed! */
  928.                         Session->OverallState = eSessionDataArrived;
  929.                     }
  930.                 /* reset read state */
  931.                 Session->ReadState = eReadIdle;
  932.             }
  933.         /* if the session has closed with the previous read, don't retrigger the read */
  934.         /* but instead just exit */
  935.         if (Session->OverallState == eSessionClosed)
  936.             {
  937.                 return;
  938.             }
  939.         /* retrigger the read operation on a new block */
  940.         Session->ReceiveInProgress = (BufferRec*)AllocPtrCanFail(
  941.             sizeof(BufferRec),"ReadBufferRec");
  942.         if (Session->ReceiveInProgress == NIL)
  943.             {
  944.                 return;
  945.             }
  946.         Session->ThePPCReadPBRec.ioCompletion = (ProcPtr)&MyPPCReadCompletionRoutine;
  947.         Session->ThePPCReadPBRec.sessRefNum = Session->SessionRefnum;
  948.         Session->ThePPCReadPBRec.bufferLength = BUFFERSIZE;
  949.         Session->ThePPCReadPBRec.bufferPtr = &(Session->ReceiveInProgress->Buffer[0]);
  950.         Session->ReadState = eReadInProgress; /* BEFORE the call! */
  951.         Error = PPCRead(&(Session->ThePPCReadPBRec),True/*async*/);
  952.     }
  953.  
  954.  
  955. /* utility routine to submit another write if one has completed */
  956. static void                    UpdateWrite(SessionIDType* Session)
  957.     {
  958.         OSErr                            Error;
  959.  
  960.         CheckPtrExistence(Session);
  961.         ERROR((Session->WriteState == eWriteInProgress),PRERR(ForceAbort,
  962.             "UpdateWrite:  write is already in progress"));
  963.         /* if a write has completed, then handle it */
  964.         if (Session->WriteState == eWriteFinished)
  965.             {
  966.                 /* check for session closed notification */
  967.                 if (Session->ThePPCWritePBRec.ioResult == sessClosedErr)
  968.                     {
  969.                         Session->OverallState = eSessionClosed;
  970.                     }
  971.                 ERROR((Session->ThePPCWritePBRec.ioResult != sessClosedErr)
  972.                     && (Session->ThePPCWritePBRec.ioResult != noErr),PRERR(AllowResume,
  973.                     "UpdateWrite:  error returned from PPCWrite"));
  974.                 /* reset write state stuff */
  975.                 Session->WriteState = eWriteIdle;
  976.                 /* dispose of the write buffer block */
  977.                 ReleasePtr((char*)(Session->SendInProgress));
  978.                 Session->SendInProgress = NIL;
  979.             }
  980.         /* if there is more data queued to be sent, then send it */
  981.         if (Session->SendHead != NIL)
  982.             {
  983.                 CheckPtrExistence(Session->SendHead);
  984.                 /* move the first block to the in progress register */
  985.                 Session->SendInProgress = Session->SendHead;
  986.                 /* pop the block off the queue */
  987.                 Session->SendHead = Session->SendHead->Next;
  988.                 if (Session->SendHead == NIL)
  989.                     {
  990.                         Session->SendTail = NIL;
  991.                     }
  992.                 /* construct the parameter record */
  993.                 Session->ThePPCWritePBRec.ioCompletion = (ProcPtr)&MyPPCWriteCompletionRoutine;
  994.                 Session->ThePPCWritePBRec.sessRefNum = Session->SessionRefnum;
  995.                 Session->ThePPCWritePBRec.bufferLength = Session->SendInProgress->NumBytes;
  996.                 Session->ThePPCWritePBRec.bufferPtr = &(Session->SendInProgress->Buffer[0]);
  997.                 Session->ThePPCWritePBRec.more = False; /* always false */
  998.                 Session->ThePPCWritePBRec.userData = 0;
  999.                 Session->ThePPCWritePBRec.blockCreator = 0;
  1000.                 Session->ThePPCWritePBRec.blockType = 0;
  1001.                 Session->WriteState = eWriteInProgress; /* BEFORE the call! */
  1002.                 Error = PPCWrite(&(Session->ThePPCWritePBRec),True/*async*/);
  1003.             }
  1004.     }
  1005.  
  1006.  
  1007. /* Verify that a session is still usable.  Returns True if the session is still */
  1008. /* available, or False if the remote system disconnected it.  If it returns False, */
  1009. /* then you should call NetCloseSession to dispose of the session record. */
  1010. MyBoolean                        NetIsSessionStillAlive(SessionIDType* Session)
  1011.     {
  1012.         ERROR(!Initialized,PRERR(ForceAbort,"NetIsSessionStillAlive:  not initialized"));
  1013.         CheckPtrExistence(Session);
  1014.         return (Session->OverallState != eSessionClosed);
  1015.     }
  1016.  
  1017.  
  1018. /* Obtain a string identifying the machine from which a session has been */
  1019. /* established.  The string is a non-null-terminated heap block. */
  1020. char*                                NetSessionGetRemoteMachineName(SessionIDType* Session)
  1021.     {
  1022.         char                            LocalBuffer[32 + 32 + 32 + 1 + 1];
  1023.         long                            Index;
  1024.         long                            Scan;
  1025.         char*                            Pointer;
  1026.  
  1027.         ERROR(!Initialized,PRERR(ForceAbort,
  1028.             "NetSessionGetRemoteMachineName:  not initialized"));
  1029.         CheckPtrExistence(Session);
  1030.         switch (Session->RemoteLocationName.locationKindSelector)
  1031.             {
  1032.                 default:
  1033.                     EXECUTE(PRERR(ForceAbort,
  1034.                         "NetSessionGetRemoteMachineName:  unknown locationKindSelector"));
  1035.                     break;
  1036.                 case ppcNoLocation:
  1037.                     Index = 9;
  1038.                     CopyData("localhost",LocalBuffer,9);
  1039.                     break;
  1040.                 case ppcNBPLocation:
  1041.                     Index = 0;
  1042.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1043.                         .u.nbpEntity.objStr[0]; Scan += 1)
  1044.                         {
  1045.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1046.                                 .u.nbpEntity.objStr[Scan + 1];
  1047.                         }
  1048.                     if (Session->RemoteLocationName.u.nbpEntity.typeStr[0] != 0)
  1049.                         {
  1050.                             LocalBuffer[Index++] = ':';
  1051.                         }
  1052.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1053.                         .u.nbpEntity.typeStr[0]; Scan += 1)
  1054.                         {
  1055.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1056.                                 .u.nbpEntity.typeStr[Scan + 1];
  1057.                         }
  1058.                     if (Session->RemoteLocationName.u.nbpEntity.zoneStr[0] != 0)
  1059.                         {
  1060.                             LocalBuffer[Index++] = '@';
  1061.                         }
  1062.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1063.                         .u.nbpEntity.zoneStr[0]; Scan += 1)
  1064.                         {
  1065.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1066.                                 .u.nbpEntity.zoneStr[Scan + 1];
  1067.                         }
  1068.                     break;
  1069.             }
  1070.         Pointer = AllocPtrCanFail(Index,"NBPNameString");
  1071.         if (Pointer != NIL)
  1072.             {
  1073.                 CopyData(&(LocalBuffer[0]),&(Pointer[0]),Index);
  1074.             }
  1075.         return Pointer;
  1076.     }
  1077.  
  1078.  
  1079. /* Find out how much data is waiting to be read from the port. */
  1080. long                                NetHowMuchDataToRead(SessionIDType* Session)
  1081.     {
  1082.         long                            DataCount;
  1083.         BufferRec*                BufferScan;
  1084.  
  1085.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToRead:  not initialized"));
  1086.         CheckPtrExistence(Session);
  1087.         BufferScan = Session->ReceiveHead;
  1088.         DataCount = 0;
  1089.         while (BufferScan != NIL)
  1090.             {
  1091.                 DataCount += BufferScan->NumBytes;
  1092.                 BufferScan = BufferScan->Next;
  1093.             }
  1094.         return DataCount;
  1095.     }
  1096.  
  1097.  
  1098. /* Find out how much data is waiting in local buffers to be written to a port. */
  1099. long                                NetHowMuchDataToWrite(SessionIDType* Session)
  1100.     {
  1101.         long                            DataCount;
  1102.         BufferRec*                BufferScan;
  1103.  
  1104.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToWrite:  not initialized"));
  1105.         CheckPtrExistence(Session);
  1106.         /* initiate another read if there isn't one pending */
  1107.         if (Session->ReadState == eReadIdle)
  1108.             {
  1109.                 UpdateRead(Session);
  1110.             }
  1111.         /* count data in buffers waiting to be eaten */
  1112.         BufferScan = Session->SendHead;
  1113.         DataCount = 0;
  1114.         while (BufferScan != NIL)
  1115.             {
  1116.                 DataCount += BufferScan->NumBytes;
  1117.                 BufferScan = BufferScan->Next;
  1118.             }
  1119.         return DataCount;
  1120.     }
  1121.  
  1122.  
  1123. /* Read data from a session.  It is an error to read more data than there is waiting. */
  1124. void                                NetReadData(SessionIDType* Session, char* Buffer, long NumBytes)
  1125.     {
  1126.         ERROR(!Initialized,PRERR(ForceAbort,"NetReadData:  not initialized"));
  1127.         CheckPtrExistence(Session);
  1128.         ERROR((NumBytes < 0) || (NumBytes > NetHowMuchDataToRead(Session)),
  1129.             PRERR(ForceAbort,"NetReadData:  number of bytes is out of range"));
  1130.         /* extract data from blocks */
  1131.         while (NumBytes > 0)
  1132.             {
  1133.                 long                            TempNumBytes;
  1134.                 BufferRec*                TempBuffer;
  1135.  
  1136.                 TempBuffer = Session->ReceiveHead;
  1137.                 CheckPtrExistence(TempBuffer);
  1138.                 TempNumBytes = TempBuffer->NumBytes;
  1139.                 CopyData(&(TempBuffer->Buffer[0]),&(Buffer[0]),TempNumBytes);
  1140.                 Session->ReceiveHead = TempBuffer->Next;
  1141.                 NumBytes -= TempNumBytes;
  1142.                 Buffer += TempNumBytes;
  1143.                 ReleasePtr((char*)TempBuffer);
  1144.             }
  1145.         if (Session->ReceiveHead == NIL)
  1146.             {
  1147.                 Session->ReceiveTail = NIL;
  1148.             }
  1149.         /* initiate another read if there isn't one pending */
  1150.         if (Session->ReadState == eReadIdle)
  1151.             {
  1152.                 UpdateRead(Session);
  1153.             }
  1154.     }
  1155.  
  1156.  
  1157. /* Write data to a session.  If data could not be sent without blocking, then */
  1158. /* it is locally buffered until it can be sent (that's what NetUpdate is for) */
  1159. /* returns True if successful, or False if there isn't enough memory.  if it fails, */
  1160. /* then NO data is written (i.e. data is never partially written) */
  1161. MyBoolean                        NetWriteData(SessionIDType* Session, char* Buffer, long NumBytes)
  1162.     {
  1163.         BufferRec*                LocalHead;
  1164.         BufferRec*                LocalTail;
  1165.         BufferRec*                BuffScan;
  1166.         long                            TempByteCount;
  1167.  
  1168.         ERROR(!Initialized,PRERR(ForceAbort,"NetWriteData:  not initialized"));
  1169.         CheckPtrExistence(Session);
  1170.         /* we create the buffers totally first, and only submit them if we could */
  1171.         /* allocate ALL of them */
  1172.         LocalHead = NIL;
  1173.         LocalTail = NIL;
  1174.         TempByteCount = NumBytes;
  1175.         while (TempByteCount > 0)
  1176.             {
  1177.                 BufferRec*                TempBuff;
  1178.  
  1179.                 if (TempByteCount > BUFFERSIZE)
  1180.                     {
  1181.                         TempByteCount -= BUFFERSIZE;
  1182.                     }
  1183.                  else
  1184.                     {
  1185.                         TempByteCount -= TempByteCount;
  1186.                     }
  1187.                 TempBuff = (BufferRec*)AllocPtrCanFail(sizeof(BufferRec),"BufferRec");
  1188.                 if (TempBuff == NIL)
  1189.                     {
  1190.                         while (LocalHead != NIL)
  1191.                             {
  1192.                                 /* dump ones that we already allocated */
  1193.                                 CheckPtrExistence(LocalHead);
  1194.                                 TempBuff = LocalHead;
  1195.                                 LocalHead = LocalHead->Next;
  1196.                                 ReleasePtr((char*)TempBuff);
  1197.                             }
  1198.                         return False; /* oops */
  1199.                     }
  1200.                 TempBuff->Next = NIL;
  1201.                 if (LocalTail != NIL)
  1202.                     {
  1203.                         LocalTail->Next = TempBuff;
  1204.                     }
  1205.                  else
  1206.                     {
  1207.                         LocalHead = TempBuff;
  1208.                     }
  1209.                 LocalTail = TempBuff;
  1210.             }
  1211.         /* buffers allocated, now fill them */
  1212.         TempByteCount = NumBytes;
  1213.         BuffScan = LocalHead;
  1214.         while (TempByteCount > 0)
  1215.             {
  1216.                 long                            NumBytesThisTime;
  1217.  
  1218.                 if (TempByteCount > BUFFERSIZE)
  1219.                     {
  1220.                         NumBytesThisTime = BUFFERSIZE;
  1221.                     }
  1222.                  else
  1223.                     {
  1224.                         NumBytesThisTime = TempByteCount;
  1225.                     }
  1226.                 CheckPtrExistence(BuffScan);
  1227.                 BuffScan->NumBytes = NumBytesThisTime;
  1228.                 CopyData(&(Buffer[0]),&(BuffScan->Buffer[0]),NumBytesThisTime);
  1229.                 Buffer += NumBytesThisTime;
  1230.                 TempByteCount -= NumBytesThisTime;
  1231.                 BuffScan = BuffScan->Next;
  1232.             }
  1233.         ERROR(BuffScan != NIL,PRERR(ForceAbort,
  1234.             "NetWriteData:  BuffScan not NIL after buffer fill loop"));
  1235.         /* now, tack it on the end of the write buffer list */
  1236.         if (Session->SendTail != NIL)
  1237.             {
  1238.                 Session->SendTail->Next = LocalHead;
  1239.             }
  1240.          else
  1241.             {
  1242.                 Session->SendHead = LocalHead;
  1243.             }
  1244.         Session->SendTail = LocalTail;
  1245.         /* if there is no outstanding write, then make a write */
  1246.         if (Session->WriteState == eWriteIdle)
  1247.             {
  1248.                 UpdateWrite(Session);
  1249.             }
  1250.         /* successful */
  1251.         return True;
  1252.     }
  1253.  
  1254.  
  1255. #ifdef THINK_C
  1256.     #if __option(profile)
  1257.         #define Profiling (True)
  1258.     #else
  1259.         #define Profiling (False)
  1260.     #endif
  1261.  
  1262.     #pragma options(!profile)
  1263. #endif
  1264.  
  1265. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB)
  1266.     {
  1267.         PortIDType*                Port;
  1268.  
  1269.         /* some goofy pointer arithmetic to get from a member of the struct */
  1270.         /* back to the beginning of the struct. */
  1271.         Port = (PortIDType*)((char*)PB - (long)&(((PortIDType*)NIL)->ThePPCInformPBRec));
  1272.         /* now we can access the other members */
  1273.         Port->PortState = eConnectionPending; /* indicate that PPCInform is done. */
  1274.     }
  1275.  
  1276. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB)
  1277.     {
  1278.         SessionIDType*        Session;
  1279.  
  1280.         /* some goofy pointer arithmetic to get from a member of the struct */
  1281.         /* back to the beginning of the struct. */
  1282.         Session = (SessionIDType*)((char*)PB
  1283.             - (long)&(((SessionIDType*)NIL)->ThePPCReadPBRec));
  1284.         /* now we can access the other members */
  1285.         Session->ReadState = eReadFinished;
  1286.     }
  1287.  
  1288. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB)
  1289.     {
  1290.         SessionIDType*        Session;
  1291.  
  1292.         /* some goofy pointer arithmetic to get from a member of the struct */
  1293.         /* back to the beginning of the struct. */
  1294.         Session = (SessionIDType*)((char*)PB
  1295.             - (long)&(((SessionIDType*)NIL)->ThePPCWritePBRec));
  1296.         /* now we can access the other members */
  1297.         Session->WriteState = eWriteFinished;
  1298.     }
  1299.  
  1300. #ifdef THINK_C
  1301.     #if Profiling
  1302.         #pragma options(profile)
  1303.     #endif
  1304. #endif
  1305.