DIRECTORY BasicTime USING [GMT], Commander USING [CommandProc, Register], CommanderOps USING [ArgumentVector, Failed, Parse ], Convert USING [RopeFromTime ], FS USING [defaultStreamOptions, Error, FileInfo, StreamOpen, StreamOptions ], IO USING [Close, PutFR1, PutRope, rope, STREAM ], Rope USING [Cat, Fetch, Find, Length, MaxLen, ROPE, Substr ], Tioga USING [ PropList ], TiogaAccess USING [FromFile, Get, Reader, TiogaChar ], TiogaFileIO USING [ GetParts ], TiogaVoicePrivate USING [ thrushHandle ], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc ], ViewerOps USING [AddProp, FetchProp], VoiceRope USING [Handle, Retain, VoiceRopeInterval ] ; VoiceInterestImpl: CEDAR PROGRAM IMPORTS Commander, CommanderOps, Convert, FS, -- FSBackdoor, Process, -- IO, Rope, TiogaAccess, TiogaFileIO, TiogaVoicePrivate, ViewerEvents, ViewerOps, VoiceRope EXPORTS TiogaVoicePrivate ~ BEGIN backgroundCommentary: IO.STREAM ฌ NIL; Commentate: PROC [remark: Rope.ROPE, commentary: IO.STREAM] = INLINE { IF commentary # NIL THEN commentary.PutRope[remark]; }; LocalFile: ERROR; DoFileInterests: PROC [file: Rope.ROPE, commentary: IO.STREAM] = { globalName: Rope.ROPE; voiceList: Rope.ROPE ฌ NIL; createDate: BasicTime.GMT; [globalName, createDate] ฌ GetFileNameAndDate[file, commentary]; IF NOT IsTiogaFile[globalName] THEN { Commentate[" Not a Tioga file\n", commentary]; RETURN }; voiceList ฌ GetVoiceList[globalName]; IF voiceList = NIL THEN Commentate[" No voice in file\n", commentary] ELSE RegisterFileInterests[globalName, createDate, voiceList, commentary]; }; GetFileNameAndDate: PROC [file: Rope.ROPE, commentary: IO.STREAM] RETURNS [globalName: Rope.ROPE, createDate: BasicTime.GMT] ~ { fullFName, attachedTo: Rope.ROPE; keep: CARDINAL; [fullFName: fullFName, attachedTo: attachedTo, keep: keep, created: createDate] ฌ FS.FileInfo[file ! FS.Error => GOTO quit]; -- if there's something wrong with the file, do nothing. Presumably whoever called whatever triggered us will complain. Commentate[IO.PutFR1["File %g:\n", IO.rope[fullFName]], commentary]; IF attachedTo = NIL THEN { globalName ฌ fullFName; Commentate["File is global:\n", commentary] } ELSE { globalName ฌ attachedTo; Commentate[IO.PutFR1["File is attached to global file %g:\n", IO.rope[globalName]], commentary] }; EXITS quit => RETURN; }; IsTiogaFile: PROC [filename: Rope.ROPE] RETURNS [tiogaFile: BOOL] ~ { fileStream: IO.STREAM; streamOptions: FS.StreamOptions ฌ FS.defaultStreamOptions; streamOptions[tiogaRead] ฌ FALSE; fileStream ฌ FS.StreamOpen[fileName: filename, streamOptions: streamOptions]; tiogaFile ฌ TiogaFileIO.GetParts[fileStream].isTioga; fileStream.Close[]; }; GetVoiceList: PROC [filename: Rope.ROPE] RETURNS [voiceList: Rope.ROPE] ~ { fileStream: TiogaAccess.Reader ฌ TiogaAccess.FromFile[filename]; rootChar: TiogaAccess.TiogaChar ฌ TiogaAccess.Get[fileStream]; -- first character produces the root properties FOR rootProps: Tioga.PropList ฌ rootChar.propList, rootProps.rest WHILE rootProps # NIL DO IF rootProps.first.key = $voicelist THEN { IF voiceList = NIL THEN voiceList ฌ NARROW[rootProps.first.val, Rope.ROPE] ELSE ERROR } ENDLOOP }; RegisterFileInterests: PROC [filename: Rope.ROPE, createDate: BasicTime.GMT, voiceList: Rope.ROPE, commentary: IO.STREAM] ~ { nextVoice: Rope.ROPE; startOfID: INT ฌ 1; endOfID: INT; Commentate[" Voice message IDs are\n", commentary]; IF NOT ( voiceList.Length > 0 AND voiceList.Fetch[0] = '& ) THEN ERROR; DO endOfID ฌ voiceList.Find["&", startOfID]; nextVoice ฌ voiceList.Substr[startOfID, IF endOfID = -1 THEN Rope.MaxLen ELSE endOfID - startOfID]; IF nextVoice.Length = 0 THEN ERROR; VoiceRope.Retain[handle: TiogaVoicePrivate.thrushHandle, vr: NEW [VoiceRope.VoiceRopeInterval ฌ [nextVoice, 0, 0]], refID: filename.Cat[" ", Convert.RopeFromTime[createDate]], class: "TiogaVoice"]; Commentate[nextVoice, commentary]; Commentate["\n", commentary]; IF endOfID = -1 THEN RETURN; startOfID ฌ endOfID + 1 ENDLOOP }; RegisterVoiceInterestCmd: Commander.CommandProc = { { ENABLE { FS.Error => IF error.group # user THEN REJECT ELSE {msg ฌ error.explanation; GOTO Quit}; LocalFile => {msg ฌ "File must be global or have a global attachment"; GOTO Quit}; }; argv: CommanderOps.ArgumentVector; argv ฌ CommanderOps.Parse[cmd ! CommanderOps.Failed => {msg ฌ errorMsg; GOTO Quit}]; IF argv.argc # 2 THEN { msg ฌ "Usage: RegisterVoiceInterest inFile"; GOTO Quit; }; DoFileInterests[argv[1], cmd.out] EXITS Quit => RETURN [$Failure, msg] }}; BackgroundCommentaryInit: Commander.CommandProc ~ { backgroundCommentary ฌ cmd.out }; RegisterViewerInterests: PUBLIC PROC [viewer: ViewerClasses.Viewer, voiceList: Rope.ROPE] ~ { eventReg: ViewerEvents.EventRegistration; IF voiceList=NIL THEN { Commentate[" No voice in file\n", backgroundCommentary]; RETURN; }; ViewerOps.AddProp[viewer, $VoiceList, voiceList]; eventReg ฌ ViewerEvents.RegisterEventProc[proc: DoViewerInterests, event: save, filter: viewer, before: FALSE]; ViewerOps.AddProp[viewer, $VoiceEventReg, eventReg]; }; DoViewerInterests: ViewerEvents.EventProc ~ { globalName: Rope.ROPE; voiceList: Rope.ROPE ฌ NIL; createDate: BasicTime.GMT; [globalName, createDate] ฌ GetFileNameAndDate[viewer.file, backgroundCommentary]; voiceList ฌ NARROW[ViewerOps.FetchProp[viewer, $VoiceList]]; IF voiceList#NIL THEN RegisterFileInterests[globalName, createDate, voiceList, backgroundCommentary]; ViewerOps.AddProp[viewer, $VoiceList, NIL]; ViewerEvents.UnRegisterEventProc[proc: ViewerOps.FetchProp[viewer, $VoiceEventReg], event: save]; ViewerOps.AddProp[viewer, $VoiceEventReg, NIL]; }; Commander.Register[key: "RegisterVoiceInterest", proc: RegisterVoiceInterestCmd, doc: "RegisterVoiceInterest inFile: register loganberry interests for all voice messages in a global or globally attached file"]; Commander.Register[key: "InterestInfo", proc: BackgroundCommentaryInit, doc: "InterestInfo: registers an output stream for commentary about voice interest registration"]; END. ะ VoiceInterestImpl.mesa PCedar version only! Copyright ำ 1990, 1992 by Xerox Corporation. All rights reserved. Polle Zellweger, December 10, 1990 4:23 pm PST Swinehart, June 7, 1992 9:20 am PDT FSBackdoor USING [RemoteEvent, NextRemoteEvent ], Process USING [Detach, priorityBackground, SetPriority ], VoiceInterests A mechanism to register interest in any voice contained in a Tioga file, plus a command to register interests in any voice found in a named file. Called by explicit RegisterVoiceInterest command (both PCedar and DCedar) or by file events generated when a file is copied to a global location (DCedar only). read FS.Mesa to understand the next few lines IF keep # 0 THEN ERROR LocalFile[] -- neither a global file or a local one with global attachment COMMENT THIS OUT FOR PCEDAR ELSE { } The following code is very inefficient in the way that it deals with files: it opens the file as a stream to see if it is a Tioga file and then gets a TiogaAccess.Reader on each Tioga file, which means making a complete copy of the file into TiogaAccess.Reader format, and then reads its root properties Faster code could be produced by using the 'control' rope reader returned by TiogaFileIO.GetParts but that means writing fairly grubby code that knows a lot about the Tioga file format [see FileOps.mesa in Tioga.df if interested] in order to determine if some file is a Tioga file, we need to open it as a plain file [hence following line] and use TiogaFileIO.GetParts to determine whether it is of the correct format. GetParts also returns the text, comments and control sections of the documents if we are interested [see comments at the head of this code] the voiceRope specification in this procedure call is only used to extract the RopeID - the start and length are not used PCedar Register interests in all voice ropes contained in this viewer. A stopgap for PCedar, since there are currently no file events in PFS. 6 Dec 90 PTZ Valid in PCedar world only Need to wait until the save is complete to get the create date. PROC [viewer: Viewer, event: ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE] DCedar A mechanism to track all files copied to global file space and register interests in any voice contained in them. Uses FS file events, not yet implemented in PCedar. Since the intent is to only register interests in global files (not on local machine), explicit use of the RegisterVoiceInterest command should never mention a local file. Check in GetFileNameAndDate controls this, SHOULD NOT BE COMMENTED OUT. lastRemoteEvent: REF READONLY FSBackdoor.RemoteEvent _ NIL; MonitorCopiesToGlobal: PROC = { -- invoked as a separate process Process.SetPriority[Process.priorityBackground]; DO lastRemoteEvent _ FSBackdoor.NextRemoteEvent[lastRemoteEvent]; IF lastRemoteEvent.op = endStoring THEN DoFileInterests[lastRemoteEvent.fName, backgroundCommentary] ENDLOOP }; DCedar initialization p: PROCESS _ FORK MonitorCopiesToGlobal; TRUSTED {Process.Detach[p]}; Initialization สž–(cedarcode) style•NewlineDelimiter ™šœ™J™Jšœ ฯeœ7™BJ™.J™#Icode™—šฯk ˜ Kšœ žœžœ˜Kšœ žœ˜(Kšœ žœ"˜4Kšœžœ˜KšžœžœE˜MJšœ žœ!™1Kšžœžœ žœ˜1Jšœžœ/™˜MK˜5K˜K˜K˜—š ข œžœžœžœžœ˜KK˜@Kšœ? /˜nšžœ?žœ žœž˜Zšžœ"žœ˜+Kš žœ žœžœ žœžœ˜KKšžœžœ˜ ——Kšž˜K˜—K˜šขœžœžœžœžœžœžœ˜}Kšœžœ˜Kšœ žœ˜Kšœ žœ˜ Kšœ5˜5K˜Kš žœžœžœžœžœ˜GK˜šž˜K˜*Kšœ(žœžœ žœ˜cKšžœžœžœ˜#K˜Jšœy™yKšœ=žœ†˜ฦKšœ@˜@K˜Kšžœžœžœ˜K˜Kšž˜—K˜K˜—šขœ˜3K˜šžœ˜Kš žœ žœžœžœžœžœ˜XKšœGžœ˜R—K˜K˜K˜"KšœHžœ˜Tšžœžœ˜K˜,Kšžœ˜ K˜—Kšœ!˜!šž˜Kšœžœ˜—˜K˜——šขœ˜3K˜Kšœ˜—J™—šก™K˜šขœž œ0žœ˜]K™•Kšœ™Kšœ)˜)šžœ žœžœ˜Kšœ:˜:Kšžœ˜K˜—Kšœ1˜1šœhžœ˜oK™?—Kšœ4˜4K˜—K˜šขœ˜-Kš žœ.žœžœ žœžœ™UKšœžœ˜Kšœžœžœ˜Kšœžœ˜K˜K˜QKšœ žœ*˜<šžœ žœž˜KšœO˜O—Kšœ&žœ˜+Kšœa˜aKšœ*žœ˜/K˜—K˜—šก™J™J™ฆJ™Jšœถขœ,™๔J™šœžœžœžœ™;J™—šขœžœ  ™@Jšœ0™0šž™Jšœ>™>šžœ!žœ™(Jšœ<™<—Jšž™—J™—J™Jšฯb™J™Jšœžœžœ™(Jšžœ™K˜—šก™K˜šœา˜าK˜—Kšœช˜ช—K˜Kšžœ˜—…—ฆ+