-- Copyright (C) 1984, 1985, 1986 by Xerox Corporation. All rights reserved. -- PupToTcpTool.mesa, WIrish, 30-May-86 21:51:36 -- From: NameLookupTool.mesa -- Please don't forget to update the herald.... DIRECTORY FormSW USING [ ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem, StringItem], Heap USING [systemZone], MsgSW USING [Post], Put USING [Char, CR, Text, Line, LongDecimal], String USING [AppendChar, AppendNumber, AppendString, Copy, InvalidNumber, Length, StringToDecimal, WordsForString], Time USING [AppendCurrent], Tool USING [ Create, MakeSWsProc, MakeMsgSW, MakeFormSW, MakeFileSW, UnusedLogName], ToolWindow USING [TransitionProcType], Window USING [Handle], Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer], NameServerDefs USING [ nameStatsRequest, nameStatsReply, NameStatsEntry, nameToCacheRequest, addressToCacheRequest, hereIsCacheEntry], PupWireFormat USING [BcplToMesaLongNumber], PupDefs USING [ PupPackageMake, PupPackageDestroy, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, SecondsToTocks, GetPupContentsBytes, SetPupContentsWords, MoveStringBodyToPupBuffer, AppendPupAddress, AppendErrorPup, GetPupAddress, PupNameTrouble], PupTypes USING [PupAddress, fillInNetID, fillInSocketID, allHosts, miscSrvSoc]; PupToTcpTool: PROGRAM IMPORTS FormSW, Heap, MsgSW, Put, String, Time, Tool, Buffer, PupWireFormat, PupDefs = BEGIN OPEN PupDefs, PupTypes; z: UNCOUNTED ZONE = Heap.systemZone; msg, form, log: Window.Handle; data: LONG POINTER TO Data ← NIL; Data: TYPE = RECORD [ where: PupAddress, target: LONG STRING, name: LONG STRING, address: LONG STRING, pupOrTcp: LONG STRING]; Stats: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; hit: BOOLEAN ← FALSE; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Pup Name Server Statistics "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 2]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← NameServerDefs.nameStatsRequest; b.pup.pupID ← [0, 0]; b.pup.pupWords[0] ← 0; SetPupContentsWords[b, 0]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM NameServerDefs.nameStatsReply => BEGIN nse: LONG POINTER TO NameServerDefs.NameStatsEntry; hit ← TRUE; nse ← LOOPHOLE[@b.pup.pupWords]; PrintInfo[ "Requests"L, PupWireFormat.BcplToMesaLongNumber[nse.nameRequests]]; PrintInfo[ "Directories sent"L, PupWireFormat.BcplToMesaLongNumber[ nse.directoriesSend]]; PrintInfo[ "Cache hits"L, PupWireFormat.BcplToMesaLongNumber[nse.cacheHits]]; PrintInfo[ "Cache misses"L, PupWireFormat.BcplToMesaLongNumber[ nse.cacheMisses]]; END; ENDCASE => PrintErrorPup[b]; WriteCR[]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; Version: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; hit: BOOLEAN ← FALSE; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Pup Network Directory Version "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 2]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← netDirVersion; b.pup.pupID ← [0, 0]; b.pup.pupWords[0] ← 0; SetPupContentsWords[b, 1]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM netDirVersion => BEGIN hit ← TRUE; WriteString["Pup-network.directory version is "L]; WriteDecimal[b.pup.pupWords[0]]; IF GetPupContentsBytes[b] > 2 THEN BEGIN WriteLine["."L]; WriteString["Pup-network.big version is "L]; WriteDecimal[b.pup.pupWords[1]]; END; END; ENDCASE => PrintErrorPup[b]; WriteLine["."L]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; NameToAddress: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; hit: BOOLEAN ← FALSE; IF data.name = NIL OR data.name.length = 0 THEN BEGIN MsgSW.Post[msg, "Name needed"L]; RETURN; END; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Name=>Address "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 2]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← nameLookup; MoveStringBodyToPupBuffer[b, data.name]; b.pup.pupID ← [0, 0]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM nameIs => BEGIN i, n: CARDINAL; addresses: LONG POINTER TO ARRAY [0..0) OF PupAddress ← LOOPHOLE[@b.pup.pupBody]; hit ← TRUE; WriteString[data.name]; WriteString[" => "L]; n ← GetPupContentsBytes[b]/(2*SIZE[PupAddress]); FOR i IN [0..n) DO IF i # 0 THEN WriteString[", "L]; PrintPupAddress[@addresses[i]]; ENDLOOP; END; nameError => BEGIN hit ← TRUE; WriteString[data.name]; WriteString[" => ERROR: "L]; PrintBodyAsText[b]; END; ENDCASE => PrintErrorPup[b]; WriteCR[]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; NameToCache: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; hit: BOOLEAN ← FALSE; IF data.name = NIL OR data.name.length = 0 THEN BEGIN MsgSW.Post[msg, "Name needed"L]; RETURN; END; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Name=>CacheEntry "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 2]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← NameServerDefs.nameToCacheRequest; MoveStringBodyToPupBuffer[b, data.name]; b.pup.pupID ← [0, 0]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM NameServerDefs.hereIsCacheEntry => BEGIN hit ← TRUE; PrintCacheEntry[b]; END; nameError => BEGIN hit ← TRUE; WriteString[data.name]; WriteString[" => ERROR: "L]; PrintBodyAsText[b]; END; ENDCASE => PrintErrorPup[b]; WriteCR[]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; AddressToName: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; a: PupAddress ← [, , [0, 0]]; hit: BOOLEAN ← FALSE; IF data.address = NIL OR data.address.length = 0 THEN BEGIN MsgSW.Post[msg, "Address needed"L]; RETURN; END; GetPupAddress[ @a, data.address ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Address=>Name "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 10]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← addressLookup; b.pup.pupID ← [0, 0]; b.pup.address ← a; SetPupContentsWords[b, SIZE[PupAddress]]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM addressIs => BEGIN hit ← TRUE; WriteString[data.address]; WriteString[" => "L]; PrintBodyAsText[b]; END; nameError => BEGIN hit ← TRUE; WriteString[data.address]; WriteString[" => ERROR: "L]; PrintBodyAsText[b]; END; ENDCASE => PrintErrorPup[b]; WriteLine["."L]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; EXITS Trouble => NULL; END; AddressToCache: FormSW.ProcType = BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; a: PupAddress ← [, , [0, 0]]; hit: BOOLEAN ← FALSE; IF data.address = NIL OR data.address.length = 0 THEN BEGIN MsgSW.Post[msg, "Address needed"L]; RETURN; END; GetPupAddress[ @a, data.address ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Address=>CacheEntry "L]; IF ~FindPath[] THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 2]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← NameServerDefs.addressToCacheRequest; b.pup.pupID ← [0, 0]; b.pup.address ← a; SetPupContentsWords[b, SIZE[PupAddress]]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM NameServerDefs.hereIsCacheEntry => BEGIN hit ← TRUE; PrintCacheEntry[b]; END; nameError => BEGIN hit ← TRUE; WriteString[data.address]; WriteString[" => ERROR: "L]; PrintBodyAsText[b]; END; ENDCASE => PrintErrorPup[b]; WriteLine["."L]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; EXITS Trouble => NULL; END; XeroxClassA: CARDINAL = 13; PupToTcp: FormSW.ProcType = BEGIN a: PupAddress ← [, , [0, 0]]; hit: BOOLEAN ← FALSE; IF data.pupOrTcp = NIL OR data.pupOrTcp.length = 0 THEN BEGIN MsgSW.Post[msg, "Conversion value needed"L]; RETURN; END; GetPupAddress[ @a, data.pupOrTcp ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; WriteCR[]; WriteCurrentDateAndTime[]; WriteLine[" PUP=>TCP"L]; WriteString[data.pupOrTcp]; WriteString[" => "L]; PrintPupAddress[@a]; WriteString[" => "L]; WriteDecimal[XeroxClassA]; WriteChar['.]; WriteDecimal[a.net/100B]; WriteChar['.]; WriteDecimal[(a.net MOD 100B)*4]; WriteChar['.]; WriteDecimal[a.host]; WriteCR[]; EXITS Trouble => NULL; END; TcpToPup: FormSW.ProcType = BEGIN tempString: STRING = [50]; numberString: STRING = [20]; tcp: ARRAY [0..4) OF CARDINAL ← ALL[0]; i: CARDINAL ← 0; copyNextNumber: PROCEDURE = BEGIN j: CARDINAL ← 1; String.Copy[numberString, "0"L]; -- skip over junk WHILE (i < String.Length[tempString]) AND ((tempString.text[i] < '0) OR (tempString.text[i] > '9)) DO i ← i + 1; ENDLOOP; -- copy over number WHILE (i < String.Length[tempString]) AND (tempString.text[i] >= '0) AND (tempString.text[i] <= '9) DO String.AppendChar[numberString, tempString.text[i]]; i ← i + 1; j ← j + 1; ENDLOOP; END; String.Copy[tempString, data.pupOrTcp]; WriteCR[]; WriteCurrentDateAndTime[]; WriteLine[" TCP=>PUP"L]; WriteString[tempString]; WriteString[" => "L]; FOR j: CARDINAL IN [0..4) DO copyNextNumber[]; tcp[j] ← String.StringToDecimal[numberString ! String.InvalidNumber => {tcp[j] ← 0; CONTINUE;}]; WriteDecimal[tcp[j]]; IF j < 3 THEN WriteChar['.]; ENDLOOP; WriteString[" => "L]; IF tcp[0] # XeroxClassA THEN BEGIN WriteLine["Error: XEROX-NET is 013.rrr.rrr.rrr!"L]; RETURN; END; WriteOctal[tcp[1]*100B + tcp[2]/4]; WriteChar['#]; WriteOctal[tcp[3] + (tcp[2] MOD 4)*400B]; WriteChar['#]; BEGIN pool: Buffer.AccessHandle; soc: PupSocket; b: PupBuffer; a: PupAddress ← [, , [0, 0]]; hit: BOOLEAN ← FALSE; a.net ← LOOPHOLE[tcp[1]*100B + tcp[2]/4]; a.host ← LOOPHOLE[tcp[3] + (tcp[2] MOD 4)*400B]; WriteString[" => "L]; IF ~SilentFindPath[] THEN BEGIN WriteString["Not PUP Registered"L]; RETURN; END; pool ← Buffer.MakePool[send: 1, receive: 10]; soc ← PupSocketMake[PupTypes.fillInSocketID, data.where, SecondsToTocks[2]]; THROUGH [0..10) UNTIL hit DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.pupType ← addressLookup; b.pup.pupID ← [0, 0]; b.pup.address ← a; SetPupContentsWords[b, SIZE[PupAddress]]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO IF data.where # b.pup.source THEN BEGIN WriteString["Reply from: "L]; PrintPupAddress[@b.pup.source]; WriteCR[]; END; SELECT b.pup.pupType FROM addressIs => BEGIN hit ← TRUE; PrintBodyAsText[b]; END; nameError => BEGIN hit ← TRUE; WriteString[data.address]; WriteString[" Not found."L]; END; ENDCASE => PrintErrorPup[b]; WriteCR[]; Buffer.ReturnBuffer[b]; ENDLOOP; IF ~hit THEN MsgSW.Post[msg, "No Response that try."L]; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; END; FindPath: PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN data; data.where ← [fillInNetID, allHosts, miscSrvSoc]; IF data.target = NIL OR data.target.length = 0 THEN BEGIN WriteLine["via broadcasting on local net(s)."L]; RETURN[TRUE]; END ELSE BEGIN WriteString["from "L]; END; WriteString[target]; WriteChar['=]; GetPupAddress[ @where, target ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; PrintPupAddress[@where]; WriteLine["."L]; RETURN[TRUE]; EXITS Trouble => RETURN[FALSE]; END; SilentFindPath: PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN data; data.where ← [fillInNetID, allHosts, miscSrvSoc]; GetPupAddress[ @where, target ! PupNameTrouble => BEGIN GOTO Trouble; END]; RETURN[TRUE]; EXITS Trouble => RETURN[FALSE]; END; -- IO things WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[log, c]; END; WriteCR: PROCEDURE = BEGIN Put.CR[log]; END; WriteString: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[log, s]; END; WriteLine: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[log, s]; END; WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END; WriteOctal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END; WriteNumber: PROCEDURE [n, radix, width: CARDINAL] = INLINE BEGIN temp: STRING = [25]; String.AppendNumber[temp, n, radix]; THROUGH [temp.length..width) DO WriteChar[' ]; ENDLOOP; WriteString[temp]; END; WriteLongDecimal: PROCEDURE [n: LONG CARDINAL] = INLINE BEGIN Put.LongDecimal[log, n]; END; D8: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 8]; END; O3: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O3Z: PROCEDURE [n: CARDINAL] = BEGIN temp: STRING = [25]; String.AppendNumber[temp, n, 8]; THROUGH [temp.length..3) DO WriteChar['0]; ENDLOOP; WriteString[temp]; END; O4: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 4]; END; O6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O9: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END; WriteCurrentDateAndTime: PROCEDURE = BEGIN temp: STRING = [20]; Time.AppendCurrent[temp]; WriteString[temp]; END; PrintInfo: PROCEDURE [s: LONG STRING, n: LONG CARDINAL] = BEGIN IF n = 0 THEN RETURN; WriteString[s]; WriteString[" = "L]; WriteLongDecimal[n]; WriteLine["."L]; END; PrintPupAddress: PROCEDURE [a: LONG POINTER TO PupAddress] = BEGIN temp: STRING = [40]; AppendPupAddress[temp, a↑]; WriteString[temp]; END; PrintErrorPup: PROCEDURE [b: PupBuffer] = BEGIN temp: STRING = [200]; AppendErrorPup[temp, b]; WriteString[temp]; END; PrintBodyAsText: PROCEDURE [b: PupBuffer] = BEGIN FOR i: CARDINAL IN [0..GetPupContentsBytes[b]) DO WriteChar[b.pup.pupChars[i]]; ENDLOOP; END; PrintCacheEntry: PROCEDURE [b: PupBuffer] = BEGIN p: LONG POINTER ← @b.pup.pupWords; n: CARDINAL ← 0; version: CARDINAL; names: CARDINAL; addrs: CARDINAL; version ← (p+n)↑; -- File version number n ← n + SIZE[CARDINAL]; names ← (p+n)↑; n ← n + SIZE[CARDINAL]; IF names = 0 THEN WriteString["??"L]; FOR i: CARDINAL IN [0..names) DO s: LONG STRING = LOOPHOLE[p+n]; words: CARDINAL ← String.WordsForString[s.length]; IF i # 0 THEN WriteString[", "L]; WriteString[s]; n ← n + words; ENDLOOP; WriteString[" <=> "L]; addrs ← (p+n)↑; n ← n + SIZE[CARDINAL]; IF addrs = 0 THEN WriteString["??"L]; FOR i: CARDINAL IN [0..addrs) DO a: LONG POINTER TO PupAddress ← LOOPHOLE[(p+n)]; IF i # 0 THEN WriteString[", "L]; PrintPupAddress[a]; n ← n + SIZE[PupAddress]; ENDLOOP; END; Init: PROCEDURE = BEGIN herald: STRING = "PUPtoTCP of 30-May-86 21:51:31"L; [] ← Tool.Create[ name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition]; END; MakeSWs: Tool.MakeSWsProc = BEGIN logFileName: STRING = [40]; msg ← Tool.MakeMsgSW[window: window, lines: 5]; form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; Tool.UnusedLogName[logFileName, "PupToTcp.log$"L]; log ← Tool.MakeFileSW[window: window, name: logFileName, allowTypeIn: FALSE]; END; MakeForm: FormSW.ClientItemsProcType = BEGIN i: INTEGER ← -1; nParams: CARDINAL = 12; items ← FormSW.AllocateItemDescriptor[nParams]; items[i ← i + 1] ← FormSW.CommandItem[tag: "Stats"L, proc: Stats, place: FormSW.newLine]; items[i ← i + 1] ← FormSW.CommandItem[tag: "Version"L, proc: Version]; items[i ← i + 1] ← FormSW.StringItem[tag: "Target"L, string: @data.target, inHeap: TRUE]; items[i ← i + 1] ← FormSW.CommandItem[ tag: "NameToAddress"L, proc: NameToAddress, place: FormSW.newLine]; items[i ← i + 1] ← FormSW.CommandItem[tag: "NameToCache"L, proc: NameToCache]; items[i ← i + 1] ← FormSW.StringItem[tag: "Name"L, string: @data.name, inHeap: TRUE]; items[i ← i + 1] ← FormSW.CommandItem[ tag: "AddressToName"L, proc: AddressToName, place: FormSW.newLine]; items[i ← i + 1] ← FormSW.CommandItem[tag: "AddressToCache"L, proc: AddressToCache]; items[i ← i + 1] ← FormSW.StringItem[tag: "Address"L, string: @data.address, inHeap: TRUE]; items[i ← i + 1] ← FormSW.CommandItem[ tag: "PupToTcp"L, proc: PupToTcp, place: FormSW.newLine]; items[i ← i + 1] ← FormSW.CommandItem[tag: "TcpToPup"L, proc: TcpToPup]; items[i ← i + 1] ← FormSW.StringItem[tag: "Convert"L, string: @data.pupOrTcp, inHeap: TRUE]; RETURN[items, TRUE]; END; AlreadyActive: ERROR = CODE; NotActive: ERROR = CODE; ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM old = inactive => BEGIN IF data # NIL THEN ERROR AlreadyActive; data ← z.NEW[Data]; data↑ ← [ where:, target: z.NEW[StringBody[20]], name: z.NEW[StringBody[20]], address: z.NEW[StringBody[20]], pupOrTcp: z.NEW[StringBody[20]] ]; String.AppendString[data.target, "ME"L]; [] ← PupDefs.PupPackageMake[]; END; new = inactive => BEGIN IF data = NIL THEN ERROR NotActive; PupDefs.PupPackageDestroy[]; z.FREE[@data.target]; z.FREE[@data.name]; z.FREE[@data.address]; z.FREE[@data]; END; ENDCASE; END; -- Main Body Init[]; END.