home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / TCPStuff.p < prev    next >
Encoding:
Text File  |  1997-05-29  |  32.7 KB  |  1,136 lines  |  [TEXT/CWIE]

  1. unit TCPStuff;
  2.  
  3. { TCPStuff © Peter Lewis, Oct 1991 }
  4. { This source is Freeware }
  5.  
  6. interface
  7.  
  8.     uses
  9.         TextUtils, TCPTypes, TCPUtils;
  10.  
  11.     const
  12.     { Amount of space to allocate for each TCP connection }
  13.         INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  14.         control_block_max = 260;
  15.         tooManyControlBlocks = -23098;
  16.  
  17.     type
  18.         OSErrPtr = ^OSErr;
  19.  
  20. { TCP connection description: }
  21.         TCPConnectionType = record
  22.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  23.                 stream: StreamPtr;
  24.                 closedone: boolean;
  25.                 laststate: integer;
  26.                 asends, asendcompletes: longint;
  27.                 closeuserptr: OSErrPtr;
  28.                 incomingPtr: Ptr;                                { Pointer into inBuf of next Byte to read. }
  29.                 incomingSize: longint;                        { Number of bytes left in inBuf. }
  30.                 buffer: Ptr;        { connection buffer. }
  31.                 inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  32.             end;
  33.         TCPConnectionPtr = ^TCPConnectionType;
  34.  
  35.         MyControlBlock = record
  36.                 tcp: TCPControlBlock;
  37.                 inuse: boolean;
  38.                 userptr: OSErrPtr;
  39.                 proc: ProcPtr;
  40.                 tcpc: TCPConnectionPtr;
  41.             end;
  42.         MyControlBlockPtr = ^MyControlBlock;
  43.  
  44.     type
  45.         UDPConnectionRecord = record
  46.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  47.                 stream: StreamPtr;
  48.                 outstanding: integer;
  49.             end;
  50.         UDPConnectionPtr = ^UDPConnectionRecord;
  51.  
  52.     var
  53.         icmp_sent_out, icmp_got_back: longint;
  54.         largest_mtu: longint;
  55.         largest_minimum_tcp_buffer_size: longint;
  56.  
  57.     procedure StartupTCPStuff;
  58.  
  59.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longint; localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  60.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longint; localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  61.     function TCPPassiveOpenDynamic (var connection: TCPConnectionPtr; buffersize: longint; var localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  62.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: StreamPtr): OSErr;
  63.     function TCPFlush (connection: TCPConnectionPtr): OSErr;
  64.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  65.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  66.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  67.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longint; var localport: integer; var remotehost: longint; var remoteport: integer; var available: longint);
  68.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  69.     function TCPCharsAvailable (connection: TCPConnectionPtr): longint;
  70.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  71.     function TCPRemoteIP (connection: TCPConnectionPtr): ipAddr;
  72.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: Ptr; readCount: integer): OSErr;
  73. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  74.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: Ptr; readCount: integer): OSErr;
  75.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longint; var b: SignedByte): OSErr;
  76.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: SignedByte;
  77.                                     charTimeOut: longint; readPtr: Ptr; readSize: longint; var readPos: longint;
  78.                                     var gottermchar: boolean): OSErr;
  79.     function TCPSend (connection: TCPConnectionPtr; writePtr: Ptr; writeCount: integer; push: boolean): OSErr;
  80.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: Ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  81.  
  82.     function UDPCreate (var connection: UDPConnectionPtr; buffer_size: longint; var localport: integer): OSErr;
  83.     function UDPRead (connection: UDPConnectionPtr; timeout: integer; var remoteIP: longint; var remoteport: integer;
  84.                                     var datap: Ptr; var datalen: integer): OSErr;
  85.     function UDPReturnBuffer (connection: UDPConnectionPtr; datap: Ptr): OSErr;
  86.     function UDPDatagramsAvailable (connection: UDPConnectionPtr): integer;
  87.     function UDPWrite (connection: UDPConnectionPtr; remoteIP: longint; remoteport: integer;
  88.                                     datap: Ptr; datalen: integer; checksum: boolean): OSErr;
  89.     function UDPRelease (var connection: UDPConnectionPtr): OSErr;
  90.     function UDPMTU (remoteIP: longint; var mtu: longint): OSErr;
  91.  
  92.     function IPGetMyIPAddr (var myIP: ipAddr): OSErr;
  93.     function IPSendICMPEcho (remotehost: ipAddr; timeout: integer; datap: Ptr; datalen: integer; complete: ProcPtr; userdata: univ Ptr; extradata: univ Ptr): OSErr;
  94. {procedure ICMPCompletion (cbp: IPControlBlockPtr; userdata: Ptr;extradata:Ptr);}
  95.  
  96.     procedure UDPZeroCB (var cb: UDPControlBlock; stream: StreamPtr; call: integer);
  97.     procedure TCPControlAsync (pbp: TCPControlBlockPtr; comp: ProcPtr);
  98. { pbp MUST be a Ptr to an XTCPControlBlock }
  99.  
  100.     function GetMinimumBufferSize (remote_ip: ipAddr; tcp: boolean): longint;
  101.     function GetBufferSize (remote_ip: ipAddr; desired: longint; tcp: boolean): longint;
  102.  
  103. implementation
  104.  
  105.     uses
  106.         Memory, Errors, Devices, Events, 
  107.         DNR, MyMathUtils, MyCallProc, MyStartup, TCPUtils, MyMemory, MyTypes;
  108.  
  109.     const
  110.         MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  111.         UDPMagic = 'UDPM';
  112.         UDPBad = '????';
  113.         dispose_block_max = 100;
  114.  
  115.     type
  116.         MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  117.  
  118. {$PUSH}
  119. {$ALIGN MAC68K}
  120.  
  121.     type
  122.         stackframe = packed record
  123.                 frameptr: Ptr;
  124.                 returnptr: Ptr;
  125.                 paramblockptr: Ptr;
  126.             end;
  127.         stackframeptr = ^stackframe;
  128.  
  129. {$ALIGN RESET}
  130. {$POP}
  131.  
  132.     var
  133.         controlblocks: MyControlBlockArray;
  134.         disposeblocks: array[1..dispose_block_max] of Ptr;
  135.         gTCPPreCompletionProc:UniversalProcPtr;
  136.         gDoIOCompletionProc:UniversalProcPtr;
  137.         gIPICMPCompletionProc:UniversalProcPtr;
  138.         gTCPSendCompleteProc:UniversalProcPtr;
  139.         gMyNotifyProc:UniversalProcPtr;
  140.         
  141.     const
  142.         max_ICMPDataArray = 100;
  143.     type
  144.         ICMPData = record
  145.                 complete: ProcPtr;
  146.                 userdata: Ptr;
  147.                 extradata: Ptr;
  148.             end;
  149.         ICMPDataArray = array[1..max_ICMPDataArray] of ICMPData;
  150.     var
  151.         icmp_data_array: ICMPDataArray;
  152.  
  153. {$IFC not GENERATINGPOWERPC}
  154.     function GetStackFrame: stackframeptr;
  155.     inline
  156.         $2E8E;
  157. {$ENDC}
  158.  
  159.     procedure TCPPreCompletionPascal(pbp: TCPControlBlockPtr);
  160.         var
  161.             prp: TCPXControlBlockPtr;
  162.     begin
  163.         prp := TCPXControlBlockPtr(ord(pbp) - 4);
  164.         if prp^.completion <> nil then begin
  165.             CallPascal04(pbp, prp^.completion);
  166.         end;
  167.     end;
  168.  
  169. {$IFC GENERATINGPOWERPC}
  170.     procedure TCPPreCompletion(pbp: TCPControlBlockPtr);
  171.     begin
  172.         TCPPreCompletionPascal(pbp);
  173.     end;
  174. {$ELSEC}
  175.     procedure TCPPreCompletion; { All C functions look like pascal paramterless procedures from the procs Point of view }
  176.     begin
  177.         TCPPreCompletionPascal(TCPControlBlockPtr(GetStackFrame^.paramblockptr));
  178.     end;
  179. {$ENDC}
  180.  
  181.     procedure TCPControlAsync (pbp: TCPControlBlockPtr; comp: ProcPtr);
  182.         var
  183.             err: OSErr;
  184.             prp: TCPXControlBlockPtr;
  185.     begin
  186.         prp := TCPXControlBlockPtr(ord(pbp) - 4);
  187.         prp^.completion := comp;
  188.         pbp^.ioCompletion := gTCPPreCompletionProc;
  189.         err := PBControlAsync(ParmBlkPtr(pbp));
  190.         if err <> noErr then begin
  191.             pbp^.ioResult := err;
  192.             if prp^.completion <> nil then begin
  193.                 CallPascal04(pbp, prp^.completion);
  194.             end;
  195.         end;
  196.     end;
  197.  
  198.     procedure IOCompletionPascal (cbp: MyControlBlockPtr);
  199.     begin
  200.         with cbp^ do begin
  201.             if userptr <> nil then begin
  202.                 userptr^ := cbp^.tcp.ioResult;
  203.             end;
  204.             inuse := false;
  205.             if proc <> nil then begin
  206.                 CallPascal04(cbp, proc);
  207.             end;
  208.         end;
  209.     end;
  210.  
  211. {$IFC GENERATINGPOWERPC}
  212.     procedure DoIOCompletion (cbp: MyControlBlockPtr);
  213.     begin
  214.         IOCompletionPascal(cbp);
  215.     end;
  216. {$ELSEC}
  217.     procedure DoIOCompletion; { All C functions look like pascal paramterless procedures from the procs Point of view }
  218.     begin
  219.         IOCompletionPascal(MyControlBlockPtr(GetStackFrame^.paramblockptr));
  220.     end;
  221. {$ENDC}
  222.  
  223.     procedure ZotBlocks;
  224.         var
  225.             i: integer;
  226.     begin
  227.         for i := 1 to dispose_block_max do begin
  228.             if disposeblocks[i] <> nil then begin
  229.                 MDisposePtr(disposeblocks[i]);
  230.             end;
  231.         end;
  232.     end;
  233.  
  234.     procedure AddBlock (p: univ Ptr);
  235. { Called at interupt level }
  236. { Must work even while ZotBlocks is in progress }
  237.         var
  238.             i: integer;
  239.     begin
  240.         for i := 1 to dispose_block_max do begin
  241.             if disposeblocks[i] = nil then begin
  242.                 disposeblocks[i] := p;
  243.                 leave;
  244.             end;
  245.         end;
  246.     end;
  247.  
  248.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  249.     begin
  250.         MZero(@cb, SizeOf(cb));
  251.         cb.tcpStream := stream;
  252.         cb.ioCRefNum := mactcp_driver_refnum;
  253.         cb.csCode := call;
  254.     end;
  255.  
  256.     function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: ProcPtr): OSErr;
  257. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  258.         var
  259.             err: OSErr;
  260.             i: integer;
  261.     begin
  262.         i := 1;
  263.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do begin
  264.             i := i + 1;
  265.         end;
  266.         cbp := controlblocks[i];
  267.         err := noErr;
  268.         if cbp = nil then begin
  269.             err := MNewPtr(cbp, SizeOf(MyControlBlock));
  270.             if err = noErr then begin
  271.                 cbp^.inuse := false;
  272.                 controlblocks[i] := cbp;
  273.             end;
  274.         end;
  275.         if (err = noErr) & not cbp^.inuse then begin
  276.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  277.             cbp^.tcp.ioCompletion := gDoIOCompletionProc;
  278.             cbp^.inuse := true;
  279.             cbp^.userptr := userptr;
  280.             cbp^.tcpc := tcpc;
  281.             cbp^.proc := proc;
  282.             if userptr <> nil then begin
  283.                 userptr^ := inProgress;
  284.             end;
  285.             GetCB := noErr;
  286.         end else begin
  287.             cbp := nil;
  288.             GetCB := memFullErr;
  289.         end;
  290.     end;
  291.  
  292.     procedure FreeCB (var cbp: MyControlBlockPtr);
  293.     begin
  294.         if cbp <> nil then begin
  295.             cbp^.inuse := false;
  296.         end;
  297.         cbp := nil;
  298.     end;
  299.  
  300.     function GetMinimumBufferSize (remote_ip: ipAddr; tcp: boolean): longint;
  301.         var
  302.             mtu: longint;
  303.             err: OSErr;
  304.             mult: integer;
  305.     begin
  306.         if tcp then begin
  307.             mult := 4;
  308.         end else begin
  309.             mult := 2;
  310.         end;
  311.         err := noErr;
  312.         if (remote_ip = 0) then begin
  313.             err := IPGetMyIPAddr(remote_ip);
  314.         end;
  315.         if err = noErr then begin
  316.             err := UDPMTU(remote_ip, mtu);
  317.         end;
  318.         if err <> noErr then begin
  319.             mtu := largest_mtu;
  320.         end;
  321.         largest_mtu := Max(mtu, largest_mtu);
  322.         largest_minimum_tcp_buffer_size := Max(4096, largest_mtu * 4 + 1024);
  323.         GetMinimumBufferSize := Max(4096, mtu * mult + 1024);
  324.     end;
  325.  
  326.     function GetBufferSize (remote_ip: ipAddr; desired: longint; tcp: boolean): longint;
  327.         var
  328.             minimum: longint;
  329.     begin
  330.         if desired = 0 then begin
  331.             desired := 6 * 1024;
  332.         end;
  333.         minimum := GetMinimumBufferSize(remote_ip, tcp);
  334.         GetBufferSize := Max(minimum, desired);
  335.     end;
  336.  
  337.     procedure DestroyConnection (var connection: TCPConnectionPtr);
  338.     begin
  339.         connection^.magic := '????';
  340.         if connection^.buffer <> nil then begin
  341.             MDisposePtr(connection^.buffer);
  342.         end;
  343.         MDisposePtr(connection);
  344.     end;
  345.  
  346.     function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  347.     begin
  348.         if (connection = nil) | (connection^.magic <> MAGICNUMBER) then begin
  349.             ValidateConnection := connectionDoesntExistErr;
  350.         end else begin
  351.             ValidateConnection := noErr;
  352.         end;
  353.     end;
  354.  
  355.     function MyPBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  356.         var
  357.             oe: OSErr;
  358.     begin
  359.         oe := PBControlAsync(ParmBlkPtr(cbp));
  360.         if oe <> noErr then begin
  361.             FreeCB(cbp);
  362.         end;
  363.         MyPBControlAsync := oe;
  364.     end;
  365.  
  366.     procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  367.     begin
  368.         if userptr <> nil then begin
  369.             if oe <> noErr then begin
  370.                 userptr^ := oe;
  371.             end;
  372.         end;
  373.     end;
  374.  
  375.     function TCPCreateConnectionForStream (var connection: TCPConnectionPtr; strm: StreamPtr): OSErr;
  376.         var
  377.             oe: OSErr;
  378.     begin
  379.         oe := MNewPtr(connection, sizeof(TCPConnectionType));
  380.         if oe = noErr then begin
  381.             with connection^ do begin
  382.                 buffer := nil;
  383.                 magic := MAGICNUMBER;
  384.                 asends := 0;
  385.                 asendcompletes := 0;
  386.                 closedone := false;
  387.                 incomingSize := 0;
  388.                 stream := strm;
  389.             end;
  390.         end;
  391.         TCPCreateConnectionForStream := oe;
  392.     end;
  393.  
  394.     function CreateStream (var connection: TCPConnectionPtr; remoteIP: longint; buffersize: longint): OSErr;
  395.         var
  396.             oe: OSErr;
  397.     begin
  398.         buffersize := GetBufferSize(remoteIP, buffersize, true);
  399.         oe := MNewPtr(connection, sizeof(TCPConnectionType));
  400.         if oe = noErr then begin
  401.             with connection^ do begin
  402.                 oe := MNewPtr(buffer, buffersize);
  403.                 if oe = noErr then begin
  404.                     magic := MAGICNUMBER;
  405.                     asends := 0;
  406.                     asendcompletes := 0;
  407.                     closedone := false;
  408.                     incomingSize := 0;
  409.                     ZotBlocks;
  410.                     oe := MTTCPCreate(stream, buffer, buffersize);
  411.                 end;
  412.             end;
  413.             if (oe <> noErr) then begin
  414.                 DestroyConnection(connection);
  415.             end;
  416.         end;
  417.         CreateStream := oe;
  418.     end;
  419.  
  420.     function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longint;var localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  421.         var
  422.             oe, ooe: OSErr;
  423.             cbp: MyControlBlockPtr;
  424.     begin
  425.         oe := CreateStream(connection, remoteIP, buffersize);
  426.         if oe = noErr then begin
  427.             with connection^ do begin
  428.                 ZotBlocks;
  429.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  430.                 if oe = noErr then begin
  431.                     cbp^.tcp.open.localport := localport;
  432.                     cbp^.tcp.open.remotehost := remoteIP;
  433.                     cbp^.tcp.open.remoteport := remoteport;
  434.                     cbp^.tcp.open.ulpTimeoutAction := -1;
  435.                     oe := MyPBControlAsync(cbp);
  436.                     if (oe=noErr) & (cs=TCPcsPassiveOpen) then begin
  437.                         while (cbp^.tcp.ioResult>=0) & (cbp^.tcp.open.localport=0) do begin
  438.                             ;
  439.                         end;
  440.                         localport:=cbp^.tcp.open.localport;
  441.                     end;
  442.                 end;
  443.                 if oe <> noErr then begin
  444.                     ooe := MTTCPRelease(stream);
  445.                     DestroyConnection(connection);
  446.                 end;
  447.             end;
  448.         end;
  449.         SetUserPtr(userptr, oe);
  450.         PAOpen := oe;
  451.     end;
  452.  
  453. { Open a connection to another machine }
  454.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longint; localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  455.     begin
  456.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  457.     end;
  458.  
  459. { Open a socket on this machine, to wait for a connection }
  460.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longint; localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  461.     begin
  462.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  463.     end;
  464.  
  465. { Open a socket on this machine, to wait for a connection }
  466.     function TCPPassiveOpenDynamic (var connection: TCPConnectionPtr; buffersize: longint; var localport: integer; remoteIP: longint; remoteport: integer; userptr: OSErrPtr): OSErr;
  467.     begin
  468.         TCPPassiveOpenDynamic := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  469.     end;
  470.  
  471.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: Ptr; readCount: integer): OSErr;
  472. { Return readCount characters from the TCP connection. }
  473. { WARNING: Doesnt Handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  474.         var
  475.             cb: TCPControlBlock;
  476.             oe: OSErr;
  477.     begin
  478.         oe := noErr;
  479.         while (oe = noErr) & (readCount > 0) do begin
  480.             ZotBlocks;
  481.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  482.             cb.receive.rcvBuff := returnPtr;
  483.             cb.receive.rcvBuffLength := readCount;
  484.             oe := PBControlSync(@cb);
  485.             longint(returnPtr) := longint(returnPtr) + cb.receive.rcvBuffLength;
  486.             readCount := readCount - cb.receive.rcvBuffLength;
  487.         end;
  488.         TCPRawReceiveChars := oe;
  489.     end;
  490.  
  491. { Return readCount characters from the TCP connection.}
  492.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: Ptr; readCount: integer): OSErr;
  493.         var
  494.             l: longint;
  495.             p: Ptr;
  496.             oe: OSErr;
  497.     begin
  498.         oe := ValidateConnection(connection);
  499.         if oe = noErr then begin
  500.             if readCount < 0 then begin
  501.                 oe := invalidLengthErr;
  502.             end else if readCount > 0 then begin
  503.                 p := returnPtr;
  504.                 with connection^ do begin
  505.                     if incomingSize > 0 then begin
  506.             { Read as much as there is or as much as we need, whichever is less. }
  507.                         if readCount < incomingSize then begin
  508.                             l := readCount;
  509.                         end else begin
  510.                             l := incomingSize;
  511.                         end;
  512.                         BlockMoveData(incomingPtr, p, l);
  513.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  514.                         incomingSize := incomingSize - l;
  515.                         p := Ptr(ord4(p) + l);
  516.                         readCount := readCount - l;
  517.                     end;
  518.                 end;
  519.                 { If there's more needed, then read it from the connection. }
  520.                 if readCount > 0 then begin
  521.                         { Issue a read and wait until it all arrives). }
  522.                     oe := TCPRawReceiveChars(connection, p, readCount);
  523.                 end;
  524.             end;
  525.         end;
  526.         TCPReceiveChars := oe;
  527.     end;
  528.  
  529.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longint; var b: SignedByte): OSErr;
  530.         { Return the next Byte in the buffer, reading more in if necessary. }
  531.         var
  532.             waitUntil: longint;
  533.             readIn: longint;
  534.             oe: OSErr;
  535.     begin
  536.         oe := ValidateConnection(connection);
  537.         if oe = noErr then begin
  538.             with connection^ do begin            { Check if we need to read in more bytes. }
  539.                 if incomingSize = 0 then begin
  540.                     if (timeout = 0) and (TCPCharsAvailable(connection) = 0) then begin
  541.                         oe := commandTimeoutErr;
  542.                     end else begin
  543.                         waitUntil := TickCount + timeout;
  544.     { keep on trying to read until we get at least one, or the time-out happens. }
  545.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  546.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  547.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  548.                                 if readIn > INCOMINGBUFSIZE then begin
  549.                                     readIn := INCOMINGBUFSIZE;
  550.                                 end;
  551.                         { Issue the read. }
  552.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  553.                                 if oe = noErr then begin
  554.                                     incomingSize := readIn;
  555.                                     incomingPtr := @inBuf;
  556.                                 end;
  557.                             end        { If not, do another round or get out, depending on the timeout condition. }
  558.                             else if TickCount > waitUntil then begin
  559.                                 oe := commandTimeoutErr;
  560.                             end;
  561.                         end;
  562.                     end;
  563.                 end;
  564.                 { Get the Byte to return. }
  565.                 if incomingSize > 0 then begin
  566.                     b := incomingPtr^;
  567.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  568.                     incomingSize := incomingSize - 1;
  569.                 end else begin
  570.                     b := 0;
  571.                 end;
  572.             end;
  573.         end;
  574.         TCPReadByte := oe;
  575.     end;
  576.  
  577. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  578. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  579. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  580. { zero, then TCPReceiveUpTo will return immediately.  }
  581.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: SignedByte;
  582.                                     charTimeOut: longint; readPtr: Ptr; readSize: longint; var readPos: longint;
  583.                                     var gottermchar: boolean): OSErr;
  584.         var
  585.             oe: OSErr;
  586.             inChar: SignedByte;
  587.             p: Ptr;
  588.     begin
  589.         oe := ValidateConnection(connection);
  590.         gottermchar := false;
  591.         if oe = noErr then begin
  592. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  593.             while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  594.                 oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  595.                 if (oe = noErr) then begin            { Put it in the result. }
  596.                     p := Ptr(ord4(readPtr) + readPos);
  597.                     p^ := inChar;
  598.                     readPos := readPos + 1;
  599.                     gottermchar := inChar = termChar;
  600.                 end;
  601.             end;
  602.             if oe = commandTimeoutErr then begin
  603.                 oe := noErr;
  604.             end;
  605.         end;
  606.         TCPReceiveUpTo := oe;
  607.     end;
  608.  
  609.     function TCPSend (connection: TCPConnectionPtr; writePtr: Ptr; writeCount: integer; push: boolean): OSErr;
  610.         var
  611.             wds: wdsType;
  612.             oe: OSErr;
  613.             cb: TCPControlBlock;
  614.     begin
  615.         oe := ValidateConnection(connection);
  616.         if oe = noErr then begin
  617.             if writeCount > 0 then begin
  618.                 wds.buffer := writePtr;
  619.                 wds.size := writeCount;
  620.                 wds.term := 0;
  621.                 ZotBlocks;
  622.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  623.                 cb.send.wds := @wds;
  624.                 cb.send.pushFlag := ord(push);
  625.                 oe := PBControlSync(@cb);
  626.             end else if writeCount < 0 then begin
  627.                 oe := invalidLengthErr;
  628.             end;
  629.         end;
  630.         TCPSend := oe;
  631.     end;
  632.  
  633.     procedure TCPSendComplete (cbp: MyControlBlockPtr);
  634.         var
  635.             oe: OSErr;
  636.     begin
  637.         AddBlock(cbp^.tcp.send.wds);
  638.         with cbp^.tcpc^ do begin
  639.             asendcompletes := asendcompletes + 1;
  640.             if (asendcompletes = asends) and closedone then begin
  641.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  642. { GetCB won't NewPtr because the completion has just released a block }
  643.                 if oe = noErr then begin
  644.                     oe := MyPBControlAsync(cbp);
  645.                 end;
  646.             end;
  647.         end;
  648.     end;
  649.  
  650.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: Ptr; writeCount: integer; push: boolean; userptr: OSErrPtr): OSErr;
  651.         type
  652.             myblock = record
  653.                     wds: wdsType;
  654.                     data: array[0..100] of Byte;
  655.                 end;
  656.             myblockptr = ^myblock;
  657.         var
  658.             oe: OSErr;
  659.             cbp: MyControlBlockPtr;
  660.             p: myblockptr;
  661.     begin
  662.         oe := ValidateConnection(connection);
  663.         if oe = noErr then begin
  664.             if writeCount > 0 then begin
  665.                 oe := MNewPtr(p, writeCount + SizeOf(wdsType));
  666.                 if oe = noErr then begin
  667.                     p^.wds.buffer := @p^.data;
  668.                     p^.wds.size := writeCount;
  669.                     p^.wds.term := 0;
  670.                     with p^.wds do begin
  671.                         BlockMoveData(writePtr, buffer, size);
  672.                     end;
  673.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, gTCPSendCompleteProc);
  674.                     cbp^.tcp.send.wds := POINTER(p);
  675.                     cbp^.tcp.send.pushFlag := ord(push);
  676.                     with connection^ do begin
  677.                         asends := asends + 1;
  678.                     end;
  679.                     oe := MyPBControlAsync(cbp);
  680.                     if oe <> noErr then begin
  681.                         MDisposePtr(p);
  682.                     end;
  683.                 end;
  684.             end else if writeCount < 0 then begin
  685.                 oe := invalidLengthErr;
  686.             end;
  687.         end;
  688.         TCPSendAsync := oe;
  689.     end;
  690.  
  691.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  692.         var
  693.             oe: OSErr;
  694.             cbp: MyControlBlockPtr;
  695.     begin
  696.         oe := ValidateConnection(connection);
  697.         if oe = noErr then begin
  698.             with connection^ do begin
  699.                 if closedone then begin
  700.                     if userptr <> nil then begin
  701.                         userptr^ := noErr;
  702.                     end;
  703.                 end else begin
  704.                     closeuserptr := userptr;
  705.                     if userptr <> nil then begin
  706.                         userptr^ := inProgress;
  707.                     end;
  708.                     closedone := true;
  709.                     if asends = asendcompletes then begin
  710.                         ZotBlocks;
  711.                         oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  712.                         if oe = noErr then begin
  713.                             oe := MyPBControlAsync(cbp);
  714.                         end;
  715.                     end;
  716.                 end;
  717.             end;
  718.         end;
  719.         SetUserPtr(userptr, oe);
  720.         TCPClose := oe;
  721.     end;
  722.  
  723.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  724.         var
  725.             oe: OSErr;
  726.             cb: TCPControlBlock;
  727.     begin
  728.         oe := ValidateConnection(connection);
  729.         if oe = noErr then begin
  730.             ZotBlocks;
  731.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  732.             oe := PBControlSync(@cb);
  733.         end;
  734.         TCPAbort := oe;
  735.     end;
  736.  
  737. { Release the TCP stream, including the buffer.}
  738.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  739.         var
  740.             oe: OSErr;
  741.     begin
  742.         oe := noErr;
  743.         oe := ValidateConnection(connection);
  744.         if oe = noErr then begin
  745.             ZotBlocks;
  746.             oe := MTTCPRelease(connection^.stream);
  747.             DestroyConnection(connection);
  748.         end;
  749.         TCPRelease := oe;
  750.     end;
  751.  
  752. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  753.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longint; var localport: integer; var remotehost: longint; var remoteport: integer; var available: longint);
  754.         var
  755.             cb: TCPControlBlock;
  756.             oe: OSErr;
  757.     begin
  758.         localhost := 0;
  759.         localport := 0;
  760.         remotehost := 0;
  761.         remoteport := 0;
  762.         available := 0;
  763.  
  764.         oe := ValidateConnection(connection);
  765.         if oe <> noErr then begin
  766.             state := 99; { Error -> Closed }
  767.         end else begin
  768.             ZotBlocks;
  769.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  770.             oe := PBControlSync(@cb);
  771.             if oe <> noErr then begin
  772.                 state := 99; { Closed }
  773.             end else begin
  774.                 state := cb.status.connectionState;
  775.                 connection^.laststate := state;
  776.                 localhost := cb.status.localhost;
  777.                 localport := cb.status.localport;
  778.                 remotehost := cb.status.remotehost;
  779.                 remoteport := cb.status.remoteport;
  780.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  781.             end;
  782.         end;
  783.     end;
  784.  
  785. { Return the state of the TCP connection.}
  786.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  787.         var
  788.             state: integer;
  789.             localhost: longint;
  790.             localport: integer;
  791.             remotehost: longint;
  792.             remoteport: integer;
  793.             available: longint;
  794.     begin
  795.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  796.         case state of
  797.             0: 
  798.                 TCPState := T_Dead;
  799.             2: 
  800.                 TCPState := T_Bored;
  801.             4, 6: 
  802.                 TCPState := T_Opening;
  803.             8: 
  804.                 TCPState := T_Established;
  805.             10, 12, 16, 18, 20: 
  806.                 TCPState := T_Closing;
  807.             14: 
  808.                 TCPState := T_PleaseClose;
  809.             98: 
  810.                 TCPState := T_WaitingForOpen;
  811.             99: 
  812.                 TCPState := T_Dead;
  813.             otherwise begin
  814.                 TCPState := T_Unknown;
  815.             end;
  816.         end;
  817.     end;
  818.  
  819. {    Return the number of characters available for reading from the TCP connection.}
  820.     function TCPCharsAvailable (connection: TCPConnectionPtr): longint;
  821.         var
  822.             state: integer;
  823.             localhost: longint;
  824.             localport: integer;
  825.             remotehost: longint;
  826.             remoteport: integer;
  827.             available: longint;
  828.     begin
  829.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  830.         TCPCharsAvailable := available;
  831.     end;
  832.  
  833.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  834.         var
  835.             state: integer;
  836.             localhost: longint;
  837.             localport: integer;
  838.             remotehost: longint;
  839.             remoteport: integer;
  840.             available: longint;
  841.     begin
  842.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  843.         TCPLocalPort := localport;
  844.     end;
  845.  
  846.     function TCPRemoteIP (connection: TCPConnectionPtr): ipAddr;
  847.         var
  848.             state: integer;
  849.             localhost: longint;
  850.             localport: integer;
  851.             remotehost: longint;
  852.             remoteport: integer;
  853.             available: longint;
  854.     begin
  855.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  856.         TCPRemoteIP := remotehost;
  857.     end;
  858.  
  859.     function TCPFlush (connection: TCPConnectionPtr): OSErr;
  860.         var
  861.             buffer: array[0..255] of SignedByte;
  862.             f: longint;
  863.             oe: OSErr;
  864.     begin
  865.         f := TCPCharsAvailable(connection);
  866.         oe := noErr;
  867.         while (f > 0) and (oe = noErr) do begin
  868.             if f > 256 then begin
  869.                 f := 256;
  870.             end;
  871.             oe := TCPReceiveChars(connection, @buffer, f);
  872.             if oe = noErr then begin
  873.                 f := TCPCharsAvailable(connection);
  874.             end;
  875.         end;
  876.         TCPFlush := oe;
  877.     end;
  878.  
  879.     procedure UDPZeroCB (var cb: UDPControlBlock; stream: StreamPtr; call: integer);
  880.     begin
  881.         MZero(@cb, SizeOf(cb));
  882.         cb.udpStream := stream;
  883.         cb.ioCRefNum := mactcp_driver_refnum;
  884.         cb.csCode := call;
  885.     end;
  886.  
  887.     procedure MyNotify (stream: StreamPtr; eventCode: integer; connection: UDPConnectionPtr; icmpMsg: Ptr);
  888.     begin
  889. {$unused(stream, icmpMsg)}
  890.         if eventCode = UDPDataArrival then begin
  891.             if connection^.magic = UDPMagic then begin
  892.                 connection^.outstanding := connection^.outstanding + 1;
  893.             end;
  894.         end;
  895.     end;
  896.  
  897.     function UDPCreate (var connection: UDPConnectionPtr; buffer_size: longint; var localport: integer): OSErr;
  898.         var
  899.             oe: OSErr;
  900.             cb: UDPControlBlock;
  901.     begin
  902.         buffer_size := GetBufferSize(0, buffer_size, false);
  903.         oe := MNewPtr(connection, SizeOf(UDPConnectionRecord) + buffer_size);
  904.         if oe = noErr then begin
  905.             connection^.magic := UDPMagic;
  906.             UDPZeroCB(cb, nil, UDPcsCreate);
  907.             cb.create.rcvBuff := Ptr(longint(connection) + SizeOf(UDPConnectionRecord));
  908.             cb.create.rcvBuffLen := buffer_size;
  909.             cb.create.notifyProc := gMyNotifyProc;
  910.             cb.create.userDataPtr := Ptr(connection);
  911.             cb.create.localport := localport;
  912.             oe := PBControlSync(@cb);
  913.             localport := cb.create.localport;
  914.             connection^.stream := cb.udpStream;
  915.             connection^.outstanding := 0;
  916.         end;
  917.         if oe <> noErr then begin
  918.             MDisposePtr( connection );
  919.         end;
  920.         UDPCreate := oe;
  921.     end;
  922.  
  923.     function UDPRead (connection: UDPConnectionPtr; timeout: integer; var remoteIP: longint; var remoteport: integer;
  924.                                     var datap: Ptr; var datalen: integer): OSErr;
  925.         var
  926.             oe: OSErr;
  927.             cb: UDPControlBlock;
  928.     begin
  929.         UDPZeroCB(cb, connection^.stream, UDPcsRead);
  930.         cb.receive.timeout := timeout;
  931.         oe := PBControlSync(@cb);
  932.         if oe = noErr then begin
  933.             connection^.outstanding := connection^.outstanding - 1;
  934.         end;
  935.         remoteIP := cb.receive.remoteip;
  936.         remoteport := cb.receive.remoteport;
  937.         datap := cb.receive.rcvBuff;
  938.         datalen := cb.receive.rcvBuffLen;
  939.         UDPRead := oe;
  940.     end;
  941.  
  942.     function UDPReturnBuffer (connection: UDPConnectionPtr; datap: Ptr): OSErr;
  943.         var
  944.             oe: OSErr;
  945.             cb: UDPControlBlock;
  946.     begin
  947.         UDPZeroCB(cb, connection^.stream, UDPcsBfrReturn);
  948.         cb.return.rcvBuff := datap;
  949.         oe := PBControlSync(@cb);
  950.         UDPReturnBuffer := oe;
  951.     end;
  952.  
  953.     function UDPDatagramsAvailable (connection: UDPConnectionPtr): integer;
  954.     begin
  955.         UDPDatagramsAvailable := connection^.outstanding;
  956.     end;
  957.  
  958.     function UDPWrite (connection: UDPConnectionPtr; remoteIP: longint; remoteport: integer;
  959.                                     datap: Ptr; datalen: integer; checksum: boolean): OSErr;
  960.         var
  961.             oe: OSErr;
  962.             cb: UDPControlBlock;
  963.             wds: wdsType;
  964.     begin
  965.         UDPZeroCB(cb, connection^.stream, UDPcsWrite);
  966.         cb.send.remoteip := remoteIP;
  967.         cb.send.remoteport := remoteport;
  968.         wds.size := datalen;
  969.         wds.buffer := datap;
  970.         wds.term := 0;
  971.         cb.send.wds := @wds;
  972.         cb.send.checksum := ord(checksum);
  973.         oe := PBControlSync(@cb);
  974.         UDPWrite := oe;
  975.     end;
  976.  
  977.     function UDPRelease (var connection: UDPConnectionPtr): OSErr;
  978.         var
  979.             oe: OSErr;
  980.             cb: UDPControlBlock;
  981.     begin
  982.         UDPZeroCB(cb, connection^.stream, UDPcsRelease);
  983.         oe := PBControlSync(@cb);
  984.         connection^.magic := UDPBad;
  985.         MDisposePtr(connection);
  986.         UDPRelease := oe;
  987.     end;
  988.  
  989.     function UDPMTU (remoteIP: longint; var mtu: longint): OSErr;
  990.         var
  991.             oe: OSErr;
  992.             cb: UDPControlBlock;
  993.     begin
  994.         UDPZeroCB(cb, nil, UDPcsMaxMTUSize);
  995.         cb.mtu.remoteip := remoteIP;
  996.         oe := PBControlSync(@cb);
  997.         mtu := BAND(cb.mtu.mtuSize, $FFFF);
  998.         UDPMTU := oe;
  999.     end;
  1000.  
  1001.     procedure IPZeroCB (var cb: IPControlBlock; call: integer);
  1002.     { Zero out the control block parameters. }
  1003.     begin
  1004.         MZero(@cb, SizeOf(cb));
  1005.         cb.ioCRefNum := mactcp_driver_refnum;
  1006.         cb.csCode := call;
  1007.     end;
  1008.  
  1009.     procedure IPCallCompletion (cbp: IPControlBlockPtr; userdata, extradata: Ptr; addr: UniversalProcPtr);
  1010.     begin
  1011.         CallPascal0444(cbp,userdata,extradata,addr);
  1012.     end;
  1013.  
  1014.     procedure IPICMPCompletionPascal (cbp: IPControlBlockPtr);
  1015.         var
  1016.             index: integer;
  1017.     begin
  1018. {        DebugStr('IPICMPCompletionPascal'); }
  1019.         icmp_got_back := icmp_got_back + 1;
  1020.         with cbp^.echoinfo do begin
  1021.             index := ord(userDataPtr);
  1022.             if (index > 0) & (icmp_data_array[index].complete <> nil) then begin
  1023.                 IPCallCompletion(cbp, icmp_data_array[index].userdata, icmp_data_array[index].extradata, icmp_data_array[index].complete);
  1024.                 icmp_data_array[index].complete := nil;
  1025.             end;
  1026.         end;
  1027.     end;
  1028.  
  1029. {$IFC GENERATINGPOWERPC}
  1030.     procedure IPICMPCompletion(cbp: IPControlBlockPtr);
  1031.     begin
  1032.         IPICMPCompletionPascal(cbp);
  1033.     end;
  1034. {$ELSEC}
  1035.     procedure IPICMPCompletion;
  1036.     begin
  1037.         IPICMPCompletionPascal(IPControlBlockPtr(GetStackFrame^.paramblockptr));
  1038.     end;
  1039. {$ENDC}
  1040.  
  1041.     function IPSendICMPEcho (remotehost: ipAddr; timeout: integer; datap: Ptr; datalen: integer; complete: ProcPtr; userdata: univ Ptr; extradata: univ Ptr): OSErr;
  1042.         var
  1043.             cb: IPControlBlock;
  1044.             i, index: integer;
  1045.             oe: OSErr;
  1046.     begin
  1047. {        DebugStr('IPSendICMPEcho');}
  1048.         index := -1;
  1049.         if complete <> nil then begin
  1050.             for i := 1 to max_ICMPDataArray do begin
  1051.                 if icmp_data_array[i].complete = nil then begin
  1052.                     index := i;
  1053.                     icmp_data_array[i].complete := complete;
  1054.                     icmp_data_array[i].userdata := userdata;
  1055.                     icmp_data_array[i].extradata := extradata;
  1056.                     leave;
  1057.                 end;
  1058.             end;
  1059.         end;
  1060.         IPZeroCB(cb, TCPcsEchoICMP);
  1061.         cb.echo.dest := remotehost;
  1062.         cb.echo.data.buffer := datap;
  1063.         cb.echo.data.size := datalen;
  1064.         cb.echo.timeout := timeout;
  1065.         cb.echo.options := nil;
  1066.         cb.echo.optlength := 0;
  1067.         cb.echo.icmpCompletion := gIPICMPCompletionProc;
  1068.         cb.echo.userDataPtr := Ptr(ord4(index)); { Avoid tickling MW bug }
  1069.         oe := PBControlSync(@cb);
  1070.         if oe = noErr then begin
  1071.             icmp_sent_out := icmp_sent_out + 1;
  1072.         end;
  1073.         IPSendICMPEcho := oe;
  1074.     end;
  1075.  
  1076.     function IPGetMyIPAddr (var myIP: ipAddr): OSErr;
  1077.         var
  1078.             cb: IPControlBlock;
  1079.             oe: OSErr;
  1080.     begin
  1081.         IPZeroCB(cb, TCPcsGetMyIP);
  1082.         oe := PBControlSync(@cb);
  1083.         myIP := cb.getmyip.ourAddress;
  1084.         IPGetMyIPAddr := oe;
  1085.     end;
  1086.  
  1087.     function InitTCPStuff(var msg: integer): OSStatus;
  1088.         var
  1089.             err: OSErr;
  1090.             i: integer;
  1091.     begin
  1092. {$unused(msg)}
  1093.         msg := ord(SMT_FailedToInitTCP);
  1094.         gTCPPreCompletionProc := NewProc(@TCPPreCompletion, uppC04ProcInfo);
  1095.         gDoIOCompletionProc := NewProc(@DoIOCompletion, uppC04ProcInfo);
  1096.         gIPICMPCompletionProc := NewProc(@IPICMPCompletion, uppC04ProcInfo);
  1097.         gTCPSendCompleteProc := NewProc(@TCPSendComplete,uppPascal04ProcInfo);
  1098.         gMyNotifyProc := NewProc(@MyNotify,uppPascal04244ProcInfo);
  1099.         err := OpenDriver('.IPP', mactcp_driver_refnum);
  1100.         for i := 1 to control_block_max do begin
  1101.             controlblocks[i] := nil;
  1102.         end;
  1103.         icmp_sent_out := 0;
  1104.         icmp_got_back := 0;
  1105.         for i := 1 to max_ICMPDataArray do begin
  1106.             icmp_data_array[i].complete := nil;
  1107.         end;
  1108.         largest_mtu := 576;
  1109.         largest_minimum_tcp_buffer_size := 4096;
  1110. {
  1111.         if err = noErr then begin
  1112.             junkl := GetMinimumBufferSize(0, true);
  1113.         end;
  1114. }
  1115.         InitTCPStuff := err;
  1116.     end;
  1117.  
  1118.     procedure FinishTCPStuff;
  1119.         var
  1120.             i: integer;
  1121.     begin
  1122.         for i := 1 to control_block_max do begin
  1123.             if controlblocks[i] <> nil then begin
  1124.                 MDisposePtr(controlblocks[i]);
  1125.             end;
  1126.         end;
  1127.     end;
  1128.  
  1129.     procedure StartupTCPStuff;
  1130.     begin
  1131.         StartupTCPUtils;
  1132.         SetStartup( InitTCPStuff, nil, 0, FinishTCPStuff );
  1133.     end;
  1134.     
  1135. end.
  1136.