DIRECTORY CardTab USING [ Create, EachPairAction, Fetch, Pairs, Ref, Store ], Commander USING [ CommandProc, Register ], CommandTool USING [ NextArgument ], Convert USING [ IntFromRope ], IO, Lark USING [ CommandEventSequence, LarkModel, Machine, SHHH ], LarkControl USING [ GetLark, SetApplicationMode ], LarkOpsRpcControl USING [ ImportNewInterface, InterfaceRecord ], LarkSmarts, LarkSmartsMonitorImpl, LarkSmartsRpcControl USING [ExportInterface, InterfaceName, UnexportInterface], LupineRuntime, MBQueue USING [ Create, Flush, Queue, QueueClientAction ], NameDB USING [ GetAttribute ], NamesRPC USING [ StartConversation ], Nice USING [ LarkConLogStream ], Process USING [ Detach, Pause, SecondsToTicks ], Pup USING [ nullSocket ], RefID USING [ ID, Reseal, Unseal ], Rope USING [Equal], RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, GetCaller, ImportFailed ], ThNet USING [ pd ], ThPartyPrivate USING [ AssignSmartsID, RegisterLocal, UnsealSmarts ], ThParty USING [ Deregister, Enable, SmartsInterfaceRecord ], Thrush USING [ Credentials, epoch, Epoch, NB, NetAddress, noAddress, nullID, PartyID, PartyType, ROPE, SmartsID, unencrypted ], ThSmartsPrivate USING [ CheckHookState, EnterLarkState, Fail, LarkInfo, LarkInfoBody, LarkParseEvent, LarkProgress, LarkReportAction, LarkSubstitution, NoteNewState, QueueLarkAction, RegisterTrunk, SetupTimeouts, SmartsInfo, SmartsInfoBody ], ThSmartsRpcControl, ThVersions, Triples USING [ Make ], TU, VoiceUtils USING [ CmdOrToken, CurrentPasskey, DNFProc, InstanceFromNetAddress, MakeRName, Problem, ProblemFR, RegisterWhereToReport, ReportFR, RnameToRspec, Rspec, WhereProc ] ; LarkSmartsInitImpl: CEDAR MONITOR LOCKS root IMPORTS IO, CardTab, Commander, CommandTool, Convert, LarkOpsRpcControl, LarkControl, LarkSmartsRpcControl, NameDB, NamesRPC, root: LarkSmartsMonitorImpl, LupineRuntime, MBQueue, Nice, Process, RefID, Rope, RPC, ThNet, ThPartyPrivate, ThParty, Thrush, ThSmartsPrivate, ThSmartsRpcControl, ThVersions, Triples, TU, VoiceUtils EXPORTS LarkSmarts, ThSmartsPrivate SHARES LarkSmartsMonitorImpl = { OPEN IO; larkInfos: CardTab.Ref _ NIL; -- Locate current or previous LarkInfos directly. Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; nullID: RefID.ID = Thrush.nullID; PartyID: TYPE = Thrush.PartyID; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; SmartsID: TYPE = Thrush.SmartsID; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; SmartsInfoBody: TYPE = ThSmartsPrivate.SmartsInfoBody; potentialServiceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]; Register: PUBLIC PROC[ shh: SHHH, -- encrypts connection oldSmartsID: Thrush.SmartsID, oldEpoch: Thrush.Epoch, machine: Lark.Machine, -- machine name for registering Lark -- model: Lark.LarkModel, authenticated: BOOL_FALSE, clientInstance: ROPE ] RETURNS [ smartsID: Thrush.SmartsID_nullID, epoch: Thrush.Epoch_Thrush.epoch ] = { larkSh: Lark.SHHH; smartsInfo: SmartsInfo; larkInfo: LarkInfo; inputQueue: MBQueue.Queue; larkInterface: LarkOpsRpcControl.InterfaceRecord_NIL; rName: ROPE _ RPC.GetCaller[shh]; serviceName: ROPE; partyType: Thrush.PartyType _ $telephone; s: VoiceUtils.Rspec _ NIL; netAddress: Thrush.NetAddress = [machine.net, machine.host, Pup.nullSocket]; larkInfo _ LarkInfoForNetAddress[netAddress].info; IF larkInfo#NIL AND ~larkInfo.failed THEN { inputQueue _ larkInfo.inputQueue; ThSmartsPrivate.Fail[larkInfo, "Lark is reregistering", FALSE]; -- already rebooted } ELSE inputQueue _ MBQueue.Create[]; IF (s_VoiceUtils.RnameToRspec[rName])#NIL AND VoiceUtils.RnameToRspec[s.simpleName] # NIL THEN rName_s.simpleName; serviceName _ NameDB.GetAttribute[rName, $service]; IF serviceName#NIL THEN partyType _ $service; larkSh _ IF NOT ThNet.pd.encryptionRequested THEN Thrush.unencrypted ELSE NamesRPC.StartConversation [ caller: myName.instance, callee: rName, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed=> { VoiceUtils.ProblemFR["Can't authenticate %g to %g", $System, NIL, rope[myName.instance], rope[rName]]; GOTO NotSmart; }]; larkInterface _ LarkOpsRpcControl.ImportNewInterface[ interfaceName: [type: "Lark.Lark", instance: clientInstance] ! RPC.ImportFailed=> { VoiceUtils.ProblemFR["Can't import Lark interface from %g", $System, NIL, rope[clientInstance]]; GOTO NotSmart; }]; larkInfo _ NEW[ThSmartsPrivate.LarkInfoBody _ [ interface: larkInterface, shh: larkSh, netAddress: netAddress, model: model, inputQueue: inputQueue, textToSpeech: Rope.Equal[serviceName, "Text-to-Speech", FALSE], scratchEv: NEW[Lark.CommandEventSequence[15]] ]]; smartsID _ ThPartyPrivate.AssignSmartsID[]; smartsInfo _ NEW[SmartsInfoBody_ [ smartsID: smartsID, partyType: partyType, ParseEvent: ThSmartsPrivate.LarkParseEvent, NoteNewStateP: ThSmartsPrivate.NoteNewState, requests: MBQueue.Create[], larkInfo: larkInfo ]]; inputQueue.QueueClientAction[ QdLarkRegistration, NEW[RegistrationInfoSpec _ [smartsInfo, rName, serviceName, clientInstance]]]; EXITS NotSmart => RETURN; }; RegistrationInfo: TYPE = REF RegistrationInfoSpec; RegistrationInfoSpec: TYPE = RECORD [ smartsInfo: SmartsInfo, rName: ROPE, serviceName: ROPE, clientInstance: ROPE ]; QdLarkRegistration: ENTRY PROC[r:REF] = { ENABLE UNWIND => NULL; nb: Thrush.NB; registrationInfo: RegistrationInfo _ NARROW[r]; smartsInfo: SmartsInfo _ registrationInfo.smartsInfo; larkInfo: LarkInfo _ smartsInfo.larkInfo; credentials: Thrush.Credentials; smartsID: Thrush.SmartsID; smarts: REF; localSmarts: ThParty.SmartsInterfaceRecord; localSmarts _ ThSmartsRpcControl.NewInterfaceRecord[]; localSmarts.clientStubProgress _ ThSmartsPrivate.LarkProgress; localSmarts.clientStubSubstitution _ ThSmartsPrivate.LarkSubstitution; localSmarts.clientStubReportAction _ ThSmartsPrivate.LarkReportAction; [nb, credentials] _ ThPartyPrivate.RegisterLocal[ rName: (SELECT smartsInfo.partyType FROM $telephone => registrationInfo.rName, ENDCASE=> registrationInfo.serviceName), type: smartsInfo.partyType, interfaceRecord: localSmarts, smartsID: smartsInfo.smartsID, -- pre-allocated so Lark could know. properties: [role: $voiceTerminal, netAddress: larkInfo.netAddress] ]; IF nb # $success OR (smartsID_credentials.smartsID) = nullID THEN { VoiceUtils.ProblemFR["Can't register Lark: %g, %g",$System,NIL, rope[registrationInfo.rName], rope[registrationInfo.clientInstance]]; GOTO NotSmart; }; larkInfo.larkSmartsInfo _ smartsInfo; smarts _ RefID.Unseal[smartsID]; IF smarts=NIL THEN ERROR; Triples.Make[$SmartsData, smarts, smartsInfo]; IF smartsInfo.partyType # $service THEN [--nb--, smartsInfo.otherSmartsID]_ ThSmartsPrivate.RegisterTrunk[ smartsID, smartsInfo, registrationInfo.rName ]; VoiceUtils.ReportFR[" (%g = %g)", $Smarts, smartsInfo, rope[registrationInfo.clientInstance], TU.RefAddr[smarts]]; IF potentialServiceProvider#NIL THEN potentialServiceProvider[credentials, smartsInfo]; RegisterLarkInfo[larkInfo]; NoteApplicationState[larkInfo, 'r]; []_ThSmartsPrivate.CheckHookState[larkInfo]; EXITS NotSmart => RETURN; }; EnableSmarts: PUBLIC ENTRY PROC[r: REF] = { ENABLE UNWIND => NULL; larkInfo: LarkInfo _ NARROW[r]; debugging: BOOL; smartsInfo: SmartsInfo _ larkInfo.larkSmartsInfo; IF larkInfo.failed THEN RETURN; -- Lost a race IF ThParty.Enable[ smartsID: smartsInfo.smartsID] # $success OR (smartsInfo.otherSmartsID # nullID AND ThParty.Enable[smartsID: smartsInfo.otherSmartsID] # $success) THEN { VoiceUtils.Problem["Could not enable", $Smarts, smartsInfo]; ThSmartsPrivate.Fail[larkInfo, "Lark failure due to failure to enable party", FALSE]; }; ThSmartsPrivate.EnterLarkState[ larkInfo, idle ]; -- Reset the Lark debugging _ Rope.Equal[case: FALSE, s2: "D", s1: NameDB.GetAttribute[ VoiceUtils.InstanceFromNetAddress[larkInfo.netAddress, NIL], $mode, "O", $larkhost]]; larkInfo.debugging _ debugging; ThSmartsPrivate.SetupTimeouts[ larkInfo, debugging ]; -- Reset the Lark NoteApplicationState[larkInfo, 'R]; }; Deregister: PUBLIC ENTRY PROC[r: REF] = { larkInfo: LarkInfo _ NARROW[r]; smartsID: SmartsID _ nullID; otherSmartsID: SmartsID _ nullID; smartsInfo: SmartsInfo _ IF larkInfo#NIL THEN larkInfo.larkSmartsInfo ELSE NIL; smarts: REF; IF smartsInfo = NIL THEN { VoiceUtils.Problem["No Smarts to Deregister", $System]; RETURN; }; NoteApplicationState[larkInfo, 'U]; smartsID _ smartsInfo.smartsID; smarts _ ThPartyPrivate.UnsealSmarts[smartsID]; VoiceUtils.ReportFR["Smarts %d (%g) is dead", $Smarts, smartsInfo, card[Reseal[smarts]], TU.RefAddr[smarts]]; smartsInfo.failed _ TRUE; smartsInfo.larkInfo.larkSmartsInfo _ NIL; IF smartsInfo.larkInfo.larkTrunkSmartsInfo#NIL THEN { smartsInfo.larkInfo.larkTrunkSmartsInfo.failed _ TRUE; smartsInfo.larkInfo.larkTrunkSmartsInfo.larkInfo _ NIL; }; smartsInfo.larkInfo.larkTrunkSmartsInfo _ NIL; smartsInfo.larkInfo _ NIL; otherSmartsID _ smartsInfo.otherSmartsID; IF otherSmartsID # nullID THEN [--nb--]_ThParty.Deregister[smartsID: otherSmartsID]; [--nb--]_ThParty.Deregister[smartsID: smartsID]; smartsInfo.requests.Flush[]; -- Abandon progress-notification queue }; NoteApplicationState: INTERNAL PROC[info: LarkInfo, state: CHAR] = { LarkControl.SetApplicationMode[LarkControl.GetLark[info.netAddress], state]; }; Login: PUBLIC PROC[shh: SHHH_, smartsID: SmartsID_, authenticated: BOOL_TRUE] = { NULL;}; WhereIsSmartsLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- = CHECKED { info: SmartsInfo=NARROW[whereData]; larkInfo: LarkInfo = IF info#NIL THEN info.larkInfo ELSE NIL; IF larkInfo#NIL THEN s_Nice.LarkConLogStream[larkInfo.netAddress]; IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s_NIL; -- use default stream unless debug viewer stream open. }; WhereIsLarkLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- = CHECKED { info: ThSmartsPrivate.LarkInfo=NARROW[whereData]; IF info#NIL THEN s_Nice.LarkConLogStream[info.netAddress]; IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s_NIL; -- only want the debug viewer stream }; WhereIsLarkLogFerSherr: VoiceUtils.WhereProc _ WhereIsLarkLog; FerSherrDefault: VoiceUtils.DNFProc=TRUSTED{ RETURN[ThNet.pd.defaultLarkReports]; }; KillLark: Commander.CommandProc = { netAddress: Thrush.NetAddress = [[173B],[Convert.IntFromRope[CommandTool.NextArgument[cmd], 8]], Pup.nullSocket]; larkInfo: LarkInfo _ LarkInfoForNetAddress[netAddress].info; IF larkInfo=NIL THEN RETURN; ThSmartsPrivate.Fail[larkInfo, "Lark failed through operator request", TRUE]; }; myName: LarkSmartsRpcControl.InterfaceName; serverPassword: RPC.EncryptionKey; LarkSmartsInit: Commander.CommandProc = { ENABLE { RPC.ExportFailed => { VoiceUtils.Problem["LarkSmarts export failed", $System]; GOTO Failed; }; }; instance: ROPE = VoiceUtils.MakeRName[style: rName, name: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]]; serverPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; myName _ [ type: "LarkSmarts.Lark", instance: instance, version: ThVersions.ThrushVR ]; VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLog, where: $Lark]; VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLogFerSherr, where: $LarkDetailed, defaultIfNotFound: FerSherrDefault]; VoiceUtils.RegisterWhereToReport[proc: WhereIsSmartsLog, where: $Smarts]; LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; LarkSmartsRpcControl.ExportInterface[ interfaceName: myName, user: instance, password: serverPassword ]; VoiceUtils.ReportFR["Export[LarkSmarts.Lark, %s]", $System, NIL, rope[myName.instance]]; EXITS Failed => LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; }; RegisterServiceProvider: PUBLIC PROC[ serviceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]] = { potentialServiceProvider _ serviceProvider; }; RegisterLarkInfo: PROC[info: LarkInfo] = { index: CARD _ LarkInfoForNetAddress[info.netAddress].index; [] _ larkInfos.Store[index, info]; }; LarkInfoForNetAddress: PROC[netAddress: Thrush.NetAddress] RETURNS[info: LarkInfo, index: CARD] = TRUSTED { netAddress.socket _ Pup.nullSocket; -- only interested in host values index _ LOOPHOLE[LONG[@netAddress], LONG POINTER TO CARD]^; IF larkInfos=NIL THEN { larkInfos _ CardTab.Create[mod: 59]; TRUSTED { Process.Detach[FORK LarkWatchDogTimerProcess[]]; }; }; info _ NARROW[larkInfos.Fetch[index].val]; }; LarkWatchDogTimerProcess: PROC = { LarkWatchDogTimer: CardTab.EachPairAction = { info: LarkInfo = NARROW[val]; IF info=NIL OR info.failed THEN RETURN[FALSE]; ThSmartsPrivate.QueueLarkAction[info, NEW[BOOL_TRUE]]; Process.Pause[Process.SecondsToTicks[ThNet.pd.larkWatchDogTimerInterval]]; RETURN[FALSE]; }; DO IF ThNet.pd.runLarkWatchDogTimer THEN [] _ larkInfos.Pairs[LarkWatchDogTimer]; Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; }; Commander.Register["LarkSmarts", LarkSmartsInit, "LarkSmarts \nInitialize and Export LarkSmarts"]; Commander.Register["KillLark", KillLark, "KillLark 110 -- deregisters lark 110"]; }. 2LarkSmartsInitImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, April 6, 1987 11:49:56 am PDT Last Edited by: Pier, May 3, 1984 2:52:59 pm PDT Copies Limited registration for supporting service registration. This handles only one service that would like to register with LarkSmarts parties (intended for synthesizer). If multiple registrations became necessary, would have to extend this. See LarkSmartsInitImpl for the point of invocation, LarkSmartsSynthImpl for an example of use. Registration/Deregistration/Initialization This procedure is synchronous with the Lark. We do enough to get started, then serialize the actual registration action with input and fail events. The LarkSmarts interface definition suggests that a Lark could present enough information during registration to be simply re-validated (kind of like a hand stamp at a dance) without any great effort. That turns out not to be worth it. If a Lark registers when already registered, we get rid of any old information about it first. Don't assume that its type has not changed from $telephone to $service or vice/versa Develop rName: the actual RName to be used to represent this party, given one that might be of the form Name.reg or Name.reg1.reg2. The latter is converted to Name.reg1. Also, obtain the name of the service represented by this Rname, if any, from the database. Above converts from, say, Swinehart.pa.lark to Swinehart.pa. Also, from Swinehart.pa to Swinehart.pa. Strips one registry if there are two or more. Get encryption taken care of Make sure we can talk to the Lark No further bad things are expected to happen. If anyone has registered, call them. They may want to register a service with our party. See LarkSmartsSynthImpl for an (the) example. Subsequent input events must relate to new world. If the phone is on-hook, mark us ready to go, and set up any once-only Lark params. Notification would loop We're debugging if the lark's mode field in the database has the value "D" or "d" Serialized with input events. Called (queued) only from LarkOutImpl.Fail. Not implemented Other Utilities Initialization, export "LarkSmarts" Same as $Lark, except doesn't print at all if debugging viewer not found. See LarkSmartsSynthImpl for an (the) example. See Register (above) for invocation of potentialServiceProvider. Polls active Larks at intervals, so that non-working ones can be discovered and removed from registered service quickly. This happens whether the Larks are involved in conversations or not. [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOLEAN] Special case requesting simplest status check. If Lark doesn't return, will trigger failure of LarkSmarts. Swinehart, May 22, 1985 5:03:50 pm PDT Cedar 6.0 New ways of defining service parties. autoAnswer (formerly hotLine), radio, and textToSpeech attributes obtained from data base. Text-To-Speech service contemplated. Deregistration works in the face of duplicate service names, etc. changes to: DIRECTORY, Register, DO changes to: DIRECTORY, Register, DO, DeregisterIfRegistered, ConsiderOne (local of DeregisterIfRegistered), Deregister Swinehart, October 28, 1985 9:55:38 am PST Names, Log => VoiceUtils, Handle => ID changes to: DIRECTORY, LarkSmartsInitImpl, ConversationID, Reseal, nullID, PartyID, SmartsID, Register, EnableSmarts, ConsiderOne (local of DeregisterIfRegistered), Deregister, SimpleDeregister, Login Swinehart, November 8, 1985 3:32:57 pm PST Accommodate new ThParty implementation changes to: DIRECTORY, Reseal, SHHH, Register, interfaceName, EnableSmarts, DeregisterIfRegistered, Deregister Swinehart, May 17, 1986 5:57:14 pm PDT Cedar 6.1 changes to: DIRECTORY, WhereIsSmartsLog, WhereIsLarkLog, KillLark Swinehart, May 25, 1986 10:09:37 pm PDT Lark => LarkOps changes to: DIRECTORY, LarkSmartsInitImpl, Register Κd˜šœ™Icodešœ Οmœ7™BJšœ<™Jšœ žœ!˜2Jšœžœ)˜@J˜ J˜Jšœžœ5˜OJ˜Jšœžœ-˜:Jšœžœ˜Jšœ žœ˜%Jšœžœ˜ Jšœžœ#˜0Jšœžœ˜Jšœžœžœ˜#J˜JšžœžœN˜WJšœžœ˜Jšœžœ1˜EJšœ<˜J˜Jš’œžœž’˜Jšœž˜JšœžœK˜TJ™”Jšœ žœ˜J˜J˜J˜Jšœ1žœ˜5Jšœžœžœ˜!Jšœ žœ˜J˜)Jšœžœ˜J˜LJ˜J™£J˜2šžœ žœžœžœ˜+J˜!Jšœ8žœŸ˜SJ˜—Jšžœ˜#J˜J™†šžœ$ž˜)Jšžœ)žœ˜HJ™J˜FJ˜Fšœ1˜1šœžœž˜(Jšœ%˜%Jšžœ!˜(—J˜J˜JšœŸ$˜CJšœC˜CJšœ˜—šžœžœ*žœ˜CJšœ;žœ˜?JšœFžœ ˜W—Jšœ-™-Jšœ%˜%Jšœ ˜ Jšžœžœžœžœ˜Jšœ.˜.šžœ!ž˜'šœŸœ˜#JšœN˜N——Jšœr˜ršžœžœžœ‘ œ ˜WJ™ˆ—šœ˜Jšœ1™1—J˜#šœ,˜,J™S—šž˜Jšœ žœ˜—Jšœ˜—J˜š   œžœžœžœžœ˜+Jšžœžœžœ˜Jšœžœ˜Jšœ žœ˜Jšœ1˜1JšžœžœžœŸ˜.Jšžœ;˜=šžœžœ"ž˜)Jšœ=žœ˜EJšœ<˜<šœNžœ˜UJšœ™—Jšœ˜—Jšœ2Ÿ˜CJ™Qšœžœ ˜,šœ˜Jšœ7žœ˜U——J˜Jšœ6Ÿ˜GJ˜#J˜J˜—š   œžœžœžœžœ˜)J™JJšœžœ˜J˜J˜!Jš œžœ žœžœžœžœ˜OJšœžœ˜ šžœžœžœ˜J˜7Jšžœ˜J˜—J˜#J˜J˜/šœB˜BJšœžœ˜*—Jšœžœ˜Jšœ%žœ˜)šžœ)žœžœ˜5Jšœ1žœ˜6Jšœ3žœ˜7J˜—Jšœ*žœ˜.Jšœžœ˜Jšœ)˜)JšžœžŸœŸœ.˜TJšœŸœ)˜0JšœŸ&˜CJ˜—J˜š œžœžœžœ˜DJ˜LJ˜J˜—š œžœžœžœ&žœžœžœ˜YJ™J˜——™J˜•StartOfExpansionO -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- šœ&ΠckNœžœ˜Jšœžœ ˜#Jš œžœžœžœžœžœ˜=Jšžœ žœ.˜BJš žœžœžœ+žœžœŸ6˜yJ˜—J˜–O -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- šœ$£Nœžœ˜Jšœžœ ˜1Jšžœžœžœ*˜:Jš žœžœžœ+žœžœŸ$˜gJ˜J˜—J–O -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- šœ>˜>šœ$žœžœ!˜TJ˜——šœ#™#J˜š œ˜#˜J˜Q—J˜