home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Applications / ircle 1.5.1 / source / TCP Libraries / TCPStuff.unit < prev    next >
Encoding:
Text File  |  1993-03-15  |  23.2 KB  |  797 lines  |  [TEXT/PJMM]

  1. unit TCPStuff;
  2.  
  3. { From Peter's PNL Libraries }
  4. { Copyright 1992 Peter N Lewis }
  5. { This source may be used for any non-commercial purposes as long as I get a mention }
  6. { in the About box and Docs of any derivative program.  It may not be used in any commercial }
  7. { application without my permission }
  8.  
  9. interface
  10.  
  11. uses
  12.     TCPTypes;
  13.  
  14. const
  15.     Minimum_TCPBUFFERSIZE = 4096;
  16.     Default_TCPBUFFERSIZE = longInt(6) * 1024;
  17.     { Amount of space to allocate for each TCP connection }
  18.     INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  19.     control_block_max = 260;
  20.     tooManyControlBlocks = -23098;
  21.  
  22. type
  23.     OSErrPtr = ^OSErr;
  24.  
  25. { TCP connection description: }
  26.     TCPConnectionType = record
  27.             magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  28.             stream: StreamPtr;
  29.             closedone: boolean;
  30.             laststate: integer;
  31.             asends, asendcompletes: longInt;
  32.             closeuserptr: OSErrPtr;
  33.             incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  34.             incomingSize: longInt;                        { Number of bytes left in inBuf. }
  35.             buffer: ptr;        { connection buffer. }
  36.             inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  37.         end;
  38.     TCPConnectionPtr = ^TCPConnectionType;
  39.  
  40.     MyControlBlock = record
  41.             tcp: TCPControlBlock;
  42.             inuse: boolean;
  43.             userptr: OSErrPtr;
  44.             proc: procPtr;
  45.             tcpc: TCPConnectionPtr;
  46.         end;
  47.     MyControlBlockPtr = ^MyControlBlock;
  48.  
  49.  
  50.     TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  51.         T_Closing, T_PleaseClose, T_Unknown);
  52.  
  53. function TCPNameToAddr (var hostName: str255; timeout: longInt): longInt;
  54. function TCPOpenResolver (var dataptr: ptr): OSErr;
  55. function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  56. procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  57. function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  58. procedure TCPCloseResolver (dataptr: ptr);
  59.  
  60. function C2PStr (s: stringPtr): stringPtr;
  61. procedure SanitizeHostName (var s: str255);
  62.  
  63. function TCPInit: OSErr;
  64. procedure TCPFinish;
  65. function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  66. function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  67. function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  68. function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  69. function TCPFlush (connection: TCPConnectionptr): OSErr;
  70. function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  71. function TCPAbort (connection: TCPConnectionPtr): OSErr;
  72. function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  73. procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  74. function TCPState (connection: TCPConnectionPtr): TCPStateType;
  75. function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  76. function TCPLocalPort (connection: TCPConnectionPtr): integer;
  77. function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  78. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  79. function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  80. function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  81. function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  82.                             charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  83.                             var gottermchar: boolean): OSErr;
  84. function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  85. function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  86.  
  87. implementation
  88.  
  89. {    Loosely based on code by Harry Chesley 12/88, thus Copyright © 1988 Apple Computer, Inc.}
  90. {    Converted to sensible pascal interface 7/91 by Peter Lewis, thus also Copyright © 1991 Peter Lewis }
  91.  
  92. const
  93.     MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  94.     dispose_block_max = 100;
  95.  
  96. type
  97.     MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  98.  
  99. var
  100.     driver_refnum: integer;
  101.     controlblocks: MyControlBlockArray;
  102.     max_dispose_block: integer;
  103.     disposeblocks: array[1..dispose_block_max] of ptr;
  104.  
  105. function C2PStr (s: StringPtr): StringPtr; { I've added this since it seems to be missing?  -oti }
  106.     var
  107.         n, i: integer;
  108.     begin
  109.         n := 0;
  110.         while s^[n] <> chr(0) do
  111.             n := succ(n);
  112.         for i := n downto 1 do
  113.             s^[i - 1] := s^[i];
  114.         s^[0] := chr(n);
  115.         C2PStr := s
  116.     end;
  117.  
  118.  
  119. procedure SanitizeHostName (var s: str255);
  120.     var
  121.         dummysp: stringPtr;
  122.     begin
  123.         dummysp := C2PStr(@s);
  124. {$PUSH}
  125. {$R-}
  126.         if s[Length(s)] = '.' then
  127.             s[0] := chr(Length(s) - 1);
  128. {$POP}
  129.     end;
  130.  
  131. function GetA6: ptr;
  132. inline
  133.     $2F4E, $0000;
  134.  
  135. procedure CallCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  136. inline
  137.     $205F, $4E90;
  138.  
  139. {$PUSH}
  140. {$D-}
  141. procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  142.     type
  143.         stackframe = packed record
  144.                 frameptr: ptr;
  145.                 returnptr: ptr;
  146.                 paramblockptr: MyControlBlockPtr;
  147.             end;
  148.         stackframeptr = ^stackframe;
  149.     var
  150.         a6: stackframeptr;
  151.         cbp: MyControlBlockPtr;
  152.     begin
  153.         a6 := stackframeptr(GetA6);
  154.         cbp := a6^.paramblockptr;
  155.         with cbp^ do begin
  156.             if userptr <> nil then
  157.                 userptr^ := cbp^.tcp.ioResult;
  158.             inuse := false;
  159.             if proc <> nil then
  160.                 CallCompletion(cbp, proc);
  161.         end;
  162.     end;
  163.  
  164. procedure ZotBlocks;
  165.     begin
  166.         while max_dispose_block > 0 do begin
  167.             DisposPtr(disposeblocks[max_dispose_block]);
  168.             max_dispose_block := max_dispose_block - 1;
  169.         end;
  170.     end;
  171.  
  172. procedure AddBlock (p: univ ptr);
  173.     begin
  174.         if max_dispose_block < dispose_block_max then begin
  175.             max_dispose_block := max_dispose_block + 1;
  176.             disposeblocks[max_dispose_block] := p;
  177.         end;
  178.     end;
  179.  
  180. procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  181.     { Zero out the control block parameters. }
  182.     var
  183.         i: integer;
  184.         p: longInt;
  185.     begin
  186.         ZotBlocks;
  187.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  188.             ptr(p)^ := 0;
  189.         cb.tcpStream := stream;
  190.         cb.ioCRefNum := driver_refnum;
  191.         cb.csCode := call;
  192.     end;
  193.  
  194. function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  195. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  196.     var
  197.         i: integer;
  198.     begin
  199.         i := 1;
  200.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  201.             i := i + 1;
  202.         cbp := controlblocks[i];
  203.         if cbp = nil then begin
  204.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  205.             if cbp <> nil then begin
  206.                 cbp^.inuse := false;
  207.                 controlblocks[i] := cbp;
  208.             end;
  209.         end;
  210.         if (cbp <> nil) & not cbp^.inuse then begin
  211.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  212.             cbp^.tcp.ioCompletion := @IOCompletion;
  213.             cbp^.inuse := true;
  214.             cbp^.userptr := userptr;
  215.             cbp^.tcpc := tcpc;
  216.             cbp^.proc := proc;
  217.             if userptr <> nil then
  218.                 userptr^ := inprogress;
  219.             GetCB := noErr;
  220.         end
  221.         else begin
  222.             cbp := nil;
  223.             GetCB := memFullErr;
  224.         end;
  225.     end;
  226.  
  227. procedure FreeCB (var cbp: MyControlBlockPtr);
  228.     begin
  229.         if cbp <> nil then
  230.             cbp^.inuse := false;
  231.         cbp := nil;
  232.     end;
  233. {$POP}
  234.  
  235. {$S Init}
  236. function TCPInit: OSErr;
  237.     var
  238.         oe: OSErr;
  239.         i: integer;
  240.     begin
  241.         max_dispose_block := 0;
  242.         oe := OpenDriver('.IPP', driver_refnum);
  243.         for i := 1 to control_block_max do
  244.             controlblocks[i] := nil;
  245.         TCPInit := oe;
  246.     end;
  247.  
  248. {$S Term}
  249. procedure TCPFinish;
  250.     var
  251.         i: integer;
  252.     begin
  253.         for i := 1 to control_block_max do
  254.             if controlblocks[i] <> nil then begin
  255.                 DisposPtr(ptr(controlblocks[i]));
  256.                 controlblocks[i] := nil;
  257.             end;
  258.     end;
  259.  
  260. {$S}
  261. procedure DestroyConnection (var connection: TCPConnectionPtr);
  262.     begin
  263.         connection^.magic := '????';
  264.         if connection^.buffer <> nil then
  265.             DisposPtr(ptr(connection^.buffer));
  266.         DisposPtr(Ptr(connection));
  267.         connection := nil;
  268.     end;
  269.  
  270. function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  271.     begin
  272.         if connection = nil then
  273.             ValidateConnection := connectionDoesntExist
  274.         else if connection^.magic <> MAGICNUMBER then
  275.             ValidateConnection := connectionDoesntExist
  276.         else
  277.             ValidateConnection := noErr;
  278.     end;
  279.  
  280. function PBControlSync (var cb: TCPControlBlock): OSErr;
  281.     begin
  282.         PBControlSync := PBControl(@cb, false);
  283.     end;
  284.  
  285. {$PUSH}
  286. {$D-}
  287. function PBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  288.     var
  289.         oe: OSErr;
  290.     begin
  291.         oe := PBControl(ParmBlkPtr(cbp), true);
  292.         if oe <> noErr then
  293.             FreeCB(cbp);
  294.         PBControlAsync := oe;
  295.     end;
  296. {$POP}
  297.  
  298. function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  299.     var
  300.         cb: TCPControlBlock;
  301.         oe: OSErr;
  302.     begin
  303.         ZeroCB(cb, nil, TCPcsGetMyIP);
  304.         oe := PBControlSync(cb);
  305.         myIP := cb.getmyip.ourAddress;
  306.         TCPGetMyIPAddr := oe;
  307.     end;
  308.  
  309. procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  310.     begin
  311.         if userptr <> nil then begin
  312.             if oe <> noErr then
  313.                 userptr^ := oe;
  314.         end;
  315.     end;
  316.  
  317. function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: streamPtr): OSErr;
  318.     var
  319.         oe: OSErr;
  320.     begin
  321.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  322.         if connection = nil then
  323.             oe := memFullErr
  324.         else begin
  325.             oe := noErr;
  326.             with connection^ do begin
  327.                 buffer := nil;
  328.                 magic := MAGICNUMBER;
  329.                 asends := 0;
  330.                 asendcompletes := 0;
  331.                 closedone := false;
  332.                 incomingSize := 0;
  333.                 stream := strm;
  334.             end;
  335.         end;
  336.         if (oe <> noErr) and (connection <> nil) then
  337.             DestroyConnection(connection);
  338.         TCPCreateConnectionForStream := oe;
  339.     end;
  340.  
  341. function CreateStream (var connection: TCPConnectionPtr; buffersize: longInt): OSErr;
  342.     var
  343.         oe: OSErr;
  344.         cb: TCPControlBlock;
  345.     begin
  346.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  347.         if connection = nil then
  348.             oe := memFullErr
  349.         else
  350.             with connection^ do begin
  351.                 buffer := NewPtr(buffersize);
  352.                 if buffer = nil then begin
  353.                     oe := memFullErr;
  354.                     DisposPtr(ptr(connection));
  355.                     connection := nil;
  356.                 end
  357.                 else begin
  358.                     magic := MAGICNUMBER;
  359.                     asends := 0;
  360.                     asendcompletes := 0;
  361.                     closedone := false;
  362.                     incomingSize := 0;
  363.                     ZeroCB(cb, nil, TCPcsCreate);
  364.                     cb.create.rcvBuff := buffer;
  365.                     cb.create.rcvBuffLen := buffersize;
  366.                     oe := PBControlSync(cb);
  367.                     stream := cb.tcpStream;
  368.                 end;
  369.             end;
  370.         if (oe <> noErr) and (connection <> nil) then
  371.             DestroyConnection(connection);
  372.         CreateStream := oe;
  373.     end;
  374.  
  375. function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  376.     var
  377.         oe, ooe: OSErr;
  378.         cbp: MyControlBlockPtr;
  379.         cb: TCPControlBlock;
  380.     begin
  381.         oe := CreateStream(connection, buffersize);
  382.         if oe = noErr then begin
  383.             with connection^ do begin
  384.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  385.                 if oe = noErr then begin
  386.                     cbp^.tcp.open.localPort := localPort;
  387.                     cbp^.tcp.open.remoteHost := remoteIP;
  388.                     cbp^.tcp.open.remotePort := remoteport;
  389.                     oe := PBControlAsync(cbp);
  390.                 end;
  391.                 if oe <> noErr then begin
  392.                     ZeroCB(cb, stream, TCPcsRelease);
  393.                     ooe := PBControlSync(cb);
  394.                     DestroyConnection(connection);
  395.                 end;
  396.             end;
  397.         end;
  398.         SetUserPtr(userptr, oe);
  399.         PAOpen := oe;
  400.     end;
  401.  
  402. { Open a connection to another machine }
  403. function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  404.     begin
  405.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  406.     end;
  407.  
  408. { Open a socket on this machine, to wait for a connection }
  409. function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  410.     begin
  411.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  412.     end;
  413.  
  414. function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  415. { Return readCount characters from the TCP connection. }
  416. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  417.     var
  418.         cb: TCPControlBlock;
  419.         oe: OSErr;
  420.     begin
  421.         repeat
  422.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  423.             cb.receive.rcvBuff := returnPtr;
  424.             cb.receive.rcvBuffLength := readCount;
  425.             oe := PBControlSync(cb);
  426.             longInt(returnPtr) := longInt(returnPtr) + cb.receive.rcvBuffLength;
  427.             readCount := readCount - cb.receive.rcvBuffLength;
  428.         until (oe <> noErr) or (readCount = 0);
  429.         TCPRawReceiveChars := oe;
  430.     end;
  431.  
  432. { Return readCount characters from the TCP connection.}
  433. function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  434.     var
  435.         readCountStr: Str255;
  436.         l: longInt;
  437.         p: Ptr;
  438.         oe: OSErr;
  439.         cb: TCPControlBlock;
  440.     begin
  441.         oe := ValidateConnection(connection);
  442.         if oe = noErr then
  443.             if readCount < 0 then
  444.                 oe := invalidLength
  445.             else if readCount > 0 then begin
  446.                 p := returnPtr;
  447.                 with connection^ do
  448.                     if incomingSize > 0 then begin
  449.             { Read as much as there is or as much as we need, whichever is less. }
  450.                         if readCount < incomingSize then
  451.                             l := readCount
  452.                         else
  453.                             l := incomingSize;
  454.                         BlockMove(incomingPtr, p, l);
  455.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  456.                         incomingSize := incomingSize - l;
  457.                         p := Ptr(ord4(p) + l);
  458.                         readCount := readCount - l;
  459.                     end;
  460.                 { If there's more needed, then read it from the connection. }
  461.                 if readCount > 0 then begin
  462.                         { Issue a read and wait until it all arrives). }
  463.                     oe := TCPRawReceiveChars(connection, p, readCount);
  464.                 end;
  465.             end;
  466.         TCPReceiveChars := oe;
  467.     end;
  468.  
  469. function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  470.         { Return the next byte in the buffer, reading more in if necessary. }
  471.     var
  472.         waitUntil: longInt;
  473.         readIn: longInt;
  474.         oe: OSErr;
  475.         cb: TCPControlBlock;
  476.     begin
  477.         oe := ValidateConnection(connection);
  478.         if oe = noErr then
  479.             with connection^ do begin            { Check if we need to read in more bytes. }
  480.                 if incomingSize = 0 then begin
  481.                     if (timeout = 0) and (TCPCharsAvailable(connection) = 0) then
  482.                         oe := commandTimeout
  483.                     else begin
  484.                         waitUntil := TickCount + timeout;
  485.     { keep on trying to read until we get at least one, or the time-out happens. }
  486.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  487.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  488.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  489.                                 if readIn > INCOMINGBUFSIZE then
  490.                                     readIn := INCOMINGBUFSIZE;
  491.                         { Issue the read. }
  492.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  493.                                 if oe = noErr then begin
  494.                                     incomingSize := readIn;
  495.                                     incomingPtr := @inBuf;
  496.                                 end;
  497.                             end        { If not, do another round or get out, depending on the timeout condition. }
  498.                             else if TickCount > waitUntil then begin
  499.                                 oe := commandTimeOut;
  500.                             end;
  501.                         end;
  502.                     end;
  503.                 end;
  504.                 { Get the byte to return. }
  505.                 if incomingSize > 0 then begin
  506.                     b := incomingPtr^;
  507.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  508.                     incomingSize := incomingSize - 1;
  509.                 end
  510.                 else
  511.                     b := 0;
  512.             end;
  513.         TCPReadByte := oe;
  514.     end;
  515.  
  516. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  517. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  518. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  519. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  520. { will read the entire buffer, and any characters that arrive before a timeout }
  521. function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  522.                                 charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  523.                                 var gottermchar: boolean): OSErr;
  524.     var
  525.         oe: OSErr;
  526.         inChar: SignedByte;
  527.         p: Ptr;
  528.     begin
  529.         oe := ValidateConnection(connection);
  530.         gottermchar := false;
  531.         if oe = noErr then begin
  532. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  533.             while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  534.                 oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  535.                 if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  536.                     p := Ptr(ord4(readPtr) + readPos);
  537.                     p^ := inChar;
  538.                     readPos := readPos + 1;
  539.                     gottermchar := inChar = termChar;
  540.                 end;
  541.             end;
  542.             if oe = commandTimeOut then
  543.                 oe := noErr;
  544.         end;
  545.         TCPReceiveUpTo := oe;
  546.     end;
  547.  
  548. function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean): OSErr;
  549.     var
  550.         wds: wdsType;
  551.         oe: OSErr;
  552.         cb: TCPControlBlock;
  553.         p: ptr;
  554.     begin
  555.         oe := ValidateConnection(connection);
  556.         if oe = nOErr then
  557.             if writeCount > 0 then begin
  558.                 wds.buffer := writePtr;
  559.                 wds.size := writeCount;
  560.                 wds.term := 0;
  561.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  562.                 cb.send.wds := @wds;
  563.                 cb.send.pushFalg := ord(push);
  564.                 oe := PBControlSync(cb);
  565.             end
  566.             else if writeCount < 0 then
  567.                 oe := InvalidLength;
  568.         TCPSend := oe;
  569.     end;
  570.  
  571. {$PUSH}
  572. {$D-}
  573. procedure TCPSendComplete (cbp: MyControlBlockPtr);
  574.     var
  575.         oe: OSErr;
  576.     begin
  577.         AddBlock(cbp^.tcp.send.wds);
  578.         with cbp^.tcpc^ do begin
  579.             asendcompletes := asendcompletes + 1;
  580.             if (asendcompletes = asends) and closedone then begin
  581.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  582. { GetCB won't NewPtr because the completion has just released a block }
  583.                 if oe = noErr then begin
  584.                     oe := PBControlAsync(cbp);
  585.                 end;
  586.             end;
  587.         end;
  588.     end;
  589. {$POP}
  590.  
  591. function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  592.     type
  593.         myblock = record
  594.                 wds: wdsType;
  595.                 data: array[0..100] of byte;
  596.             end;
  597.         myblockptr = ^myblock;
  598.     var
  599.         oe: OSErr;
  600.         cbp: MyControlBlockPtr;
  601.         p: myblockptr;
  602.     begin
  603.         oe := ValidateConnection(connection);
  604.         if oe = nOErr then
  605.             if writeCount > 0 then begin
  606.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  607.                 if p = nil then
  608.                     oe := memFullErr
  609.                 else begin
  610.                     p^.wds.buffer := @p^.data;
  611.                     p^.wds.size := writeCount;
  612.                     p^.wds.term := 0;
  613.                     with p^.wds do
  614.                         BlockMove(writePtr, buffer, size);
  615.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  616.                     cbp^.tcp.send.wds := POINTER(p);
  617.                     cbp^.tcp.send.pushFalg := ord(push);
  618.                     with connection^ do
  619.                         asends := asends + 1;
  620.                     oe := PBControlAsync(cbp);
  621.                     if oe <> noErr then
  622.                         DisposPtr(ptr(p));
  623.                 end;
  624.             end
  625.             else if writeCount < 0 then
  626.                 oe := InvalidLength;
  627.         TCPSendAsync := oe;
  628.     end;
  629.  
  630. function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  631.     var
  632.         oe: OSErr;
  633.         cbp: MyControlBlockPtr;
  634.     begin
  635.         oe := ValidateConnection(connection);
  636.         if oe = noErr then
  637.             with connection^ do begin
  638.                 closeuserptr := userptr;
  639.                 if userptr <> nil then
  640.                     userptr^ := inProgress;
  641.                 closedone := true;
  642.                 if asends = asendcompletes then begin
  643.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  644.                     if oe = noErr then begin
  645.                         oe := PBControlAsync(cbp);
  646.                     end;
  647.                 end;
  648.             end;
  649.         SetUserPtr(userptr, oe);
  650.         TCPClose := oe;
  651.     end;
  652.  
  653. function TCPAbort (connection: TCPConnectionPtr): OSErr;
  654.     var
  655.         oe: OSErr;
  656.         cb: TCPControlBlock;
  657.     begin
  658.         oe := ValidateConnection(connection);
  659.         if oe = noErr then begin
  660.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  661.             oe := PBControlSync(cb);
  662.         end;
  663.         TCPAbort := oe;
  664.     end;
  665.  
  666. { Release the TCP stream, including the buffer.}
  667. function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  668.     var
  669.         oe: OSErr;
  670.         cb: TCPControlBlock;
  671.     begin
  672.         oe := ValidateConnection(connection);
  673.         if oe = noErr then begin
  674.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  675.             oe := PBControlSync(cb);
  676.             DestroyConnection(connection);
  677.         end;
  678.         TCPRelease := oe;
  679.     end;
  680.  
  681. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  682. procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  683.     var
  684.         cb: TCPControlBlock;
  685.         oe: OSErr;
  686.     begin
  687.         oe := ValidateConnection(connection);
  688.         localhost := 0;
  689.         localport := 0;
  690.         remotehost := 0;
  691.         remoteport := 0;
  692.         available := 0;
  693.         if oe <> noErr then begin
  694.             state := 99; { Error -> Closed }
  695.         end
  696.         else begin
  697.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  698.             if PBControlSync(cb) <> noErr then begin
  699.                 state := 99; { Closed }
  700.             end
  701.             else begin
  702.                 state := cb.status.connectionState;
  703.                 connection^.laststate := state;
  704.                 localhost := cb.status.localhost;
  705.                 localport := cb.status.localport;
  706.                 remotehost := cb.status.remotehost;
  707.                 remoteport := cb.status.remoteport;
  708.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  709.             end;
  710.         end;
  711.     end;
  712.  
  713. { Return the state of the TCP connection.}
  714. function TCPState (connection: TCPConnectionPtr): TCPStateType;
  715.     var
  716.         state: integer;
  717.         localhost: longInt;
  718.         localport: integer;
  719.         remotehost: longInt;
  720.         remoteport: integer;
  721.         available: longInt;
  722.     begin
  723.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  724.         case state of
  725.             0: 
  726.                 TCPState := T_Closed;
  727.             2: 
  728.                 TCPState := T_Listening;
  729.             4, 6: 
  730.                 TCPState := T_Opening;
  731.             8: 
  732.                 TCPState := T_Established;
  733.             10, 12, 16, 18, 20: 
  734.                 TCPState := T_Closing;
  735.             14: 
  736.                 TCPState := T_PleaseClose;
  737.             98: 
  738.                 TCPState := T_WaitingForOpen;
  739.             99: 
  740.                 TCPState := T_Closed;
  741.             otherwise
  742.                 TCPState := T_Unknown;
  743.         end;
  744.     end;
  745.  
  746. {    Return the number of characters available for reading from the TCP connection.}
  747. function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  748.     var
  749.         state: integer;
  750.         localhost: longInt;
  751.         localport: integer;
  752.         remotehost: longInt;
  753.         remoteport: integer;
  754.         available: longInt;
  755.     begin
  756.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  757.         TCPCharsAvailable := available;
  758.     end;
  759.  
  760. function TCPLocalPort (connection: TCPConnectionPtr): integer;
  761.     var
  762.         state: integer;
  763.         localhost: longInt;
  764.         localport: integer;
  765.         remotehost: longInt;
  766.         remoteport: integer;
  767.         available: longInt;
  768.     begin
  769.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  770.         TCPLocalPort := localport;
  771.     end;
  772.  
  773. function TCPFlush (connection: TCPConnectionptr): OSErr;
  774.     var
  775.         buffer: array[0..255] of signedByte;
  776.         f: longInt;
  777.         oe: OSErr;
  778.     begin
  779.         f := TCPCharsAvailable(connection);
  780.         oe := noErr;
  781.         while (f > 0) and (oe = noErr) do begin
  782.             if f > 256 then
  783.                 f := 256;
  784.             oe := TCPReceiveChars(connection, @buffer, f);
  785.             if oe = noErr then
  786.                 f := TCPCharsAvailable(connection);
  787.         end;
  788.         TCPFlush := oe;
  789.     end;
  790.  
  791. end.
  792. function TCPNameToAddr (var hostName: str255; timeout: longInt; var hostFile: str255): longInt;
  793. function TCPOpenResolver (var hostFile: str255; var dataptr: ptr): OSErr;
  794.     function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  795.         procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  796.             function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  797.                 procedure TCPCloseResolver (dataptr: ptr);