VoiceInterestImpl.mesa
a mechanism to track all files copied to global file space and register interest in any voice contained in them, plus a command to register interests in any voice found in a named file [which must be global or with global attachment: the interest is registered for the global name]
Ades, April 14, 1986 3:11:27 pm PST
Doug Terry, July 1, 1986 10:20:15 am PDT
the 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 FileReader.FromStream but that means writing fairly grubby code that knows a lot about the tioga file format [see FileOps.mesa in tioga.df if interested]
DIRECTORY
Rope USING [ROPE, MaxLen, Substr, Length, Fetch, Find, Cat],
BasicTime USING [GMT],
IO USING [STREAM, PutF, PutFR, rope],
FileReader USING [FromStream],
FS USING [FileInfo, Error, StreamOptions, defaultStreamOptions, StreamOpen],
FSBackdoor USING [RemoteEvent, NextRemoteEvent],
Commander USING [CommandProc, Register],
Convert USING [RopeFromTime],
CommandTool USING [ArgumentVector, Parse, Failed],
Process USING [Detach, SetPriority, priorityBackground],
TiogaAccess USING [FromFile, Reader, TiogaChar, Get],
VoiceRope USING [Retain, VoiceRopeInterval],
Atom USING [PropList],
VoiceInText USING [thrushHandle];
VoiceInterestImpl: CEDAR PROGRAM IMPORTS Rope, IO, FileReader, FS, FSBackdoor, Commander, Convert, CommandTool, Process, TiogaAccess, VoiceRope, VoiceInText = BEGIN
LocalFile: ERROR;
backgroundCommentary: IO.STREAMNIL;
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 RegisterInterest[lastRemoteEvent.fName, backgroundCommentary]
ENDLOOP
};
RegisterInterest: PROC [file: Rope.ROPE, commentary: IO.STREAMNIL] = {
fullFName, attachedTo, globalName: Rope.ROPE;
keep: CARDINAL;
voiceList: Rope.ROPENIL;
createDate: BasicTime.GMT;
Commentate: PROC [remark: Rope.ROPE] = { IF commentary # NIL THEN commentary.PutF[remark] };
[fullFName: fullFName, attachedTo: attachedTo, keep: keep, created: createDate] ← FS.FileInfo[file]; -- read FS.Mesa to understand the next few lines
Commentate[IO.PutFR["File %g:\n", IO.rope[fullFName]]];
IF attachedTo = NIL
THEN
{ IF keep # 0 THEN ERROR LocalFile[] -- neither a global file or a local one with global attachment
ELSE { globalName ← fullFName; Commentate["File is global:\n"] }
}
ELSE { globalName ← attachedTo; Commentate[IO.PutFR["File is attached to global file %g:\n", IO.rope[globalName]]] };
{ fileStream: IO.STREAM;
streamOptions: FS.StreamOptions ← FS.defaultStreamOptions;
tiogaFile: BOOLEAN;
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 FileReader.FromStream to determine whether it is of the correct format. FromStream also returns the text, comments and control sections of the documents if we are interested [see comments at the head of this code]
streamOptions[tiogaRead] ← FALSE;
fileStream ← FS.StreamOpen[fileName: fullFName, streamOptions: streamOptions];
[tiogaFile: tiogaFile] ← FileReader.FromStream[fileStream, LAST[INT]];
IF ~tiogaFile THEN
{ Commentate[" Not a tioga file\n"];
RETURN
}
};
{ fileStream: TiogaAccess.Reader ← TiogaAccess.FromFile[fullFName];
rootChar: TiogaAccess.TiogaChar ← TiogaAccess.Get[fileStream]; -- first character produces the root properties
FOR rootProps: Atom.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
};
IF voiceList = NIL
THEN
{ Commentate[" No voice in file\n"];
RETURN
};
Commentate[" Voice message IDs are\n"];
{ nextVoice: Rope.ROPE;
startOfID: INT ← 1;
endOfID: INT;
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;
the voiceRope specification in this procedure call is only used to extract the RopeID - the start and length are not used
VoiceRope.Retain[handle: VoiceInText.thrushHandle, vr: NEW [VoiceRope.VoiceRopeInterval ← [nextVoice, 0, 0]], refID: globalName.Cat[" ", Convert.RopeFromTime[createDate]], class: "TiogaVoice"];
Commentate[nextVoice]; Commentate["\n"];
IF endOfID = -1 THEN EXIT;
startOfID ← endOfID + 1
ENDLOOP
}
};
RegisterVoiceInterest: 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: CommandTool.ArgumentVector;
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => {msg ← errorMsg; GOTO Quit}];
IF argv.argc # 2 THEN {
msg ← "Usage: RegisterVoiceInterest inFile";
GOTO Quit;
};
RegisterInterest[argv[1], cmd.out]
EXITS
Quit => RETURN [$Failure, msg]
}};
backgroundCommentaryInit: Commander.CommandProc = { backgroundCommentary ← cmd.out };
p: PROCESSFORK MonitorCopiesToGlobal;
TRUSTED {Process.Detach[p]};
Commander.Register[key: "RegisterVoiceInterest", proc: RegisterVoiceInterest, 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.
Doug Terry, July 1, 1986 10:13:33 am PDT
changes to: RegisterInterest
Doug Terry, July 1, 1986 10:20:15 am PDT
changes to: RegisterInterest, END