File: NuthatchImpl.mesa
Last Edited by: Swinehart, November 29, 1984 3:15:05 pm PST
DIRECTORY
Commander USING [ CommandProc, Register ],
CommandTool USING [ ArgumentVector, Parse ],
GVBasics USING [RName],
IO,
Nice USING [ View ],
Nuthatch,
NuthatchDB USING [CloseTransaction, GetVoiceFileEntry, GetVoiceFileID, InitializeDB],
NuthatchLog USING [InitializeLog, WriteLogEntry],
NuthatchUpdater USING [DoLogEntries],
Rope USING [Concat, ROPE ],
BasicTime USING [GMT, Now, nullGMT],
Thrush USING [nullKey],
UserProfile USING [ Token ]
;
NuthatchImpl: CEDAR PROGRAM
IMPORTS BasicTime, Commander, CommandTool, IO, Nice, NuthatchDB, NuthatchLog, NuthatchUpdater, Rope, UserProfile
EXPORTS Nuthatch = {
OPEN Nuthatch;
currentNUH: PUBLIC NuthatchUserHandle;
pd: PUBLIC REF Nuthatch.PDNEW[Nuthatch.PD];
defaultType: PUBLIC Rope.ROPE;
updateProcess: PUBLIC PROCESS;
tune: PUBLIC Tune;
samples: PUBLIC INT;
startSample: PUBLIC INT;
key: PUBLIC EncryptionKey;
logStream: IO.STREAMNIL;
LogEntryType: TYPE = {increment, decrement, update};
RefIDType: TYPE = IDType;
RefID: TYPE = ID;
InitializeNuthatch: PUBLIC PROC[
userName: GVBasics.RName←NIL, 
logFileName: Rope.ROPENIL,
RefIDType: Rope.ROPENIL,
close: BOOLTRUE] RETURNS [success: BOOLTRUE, nuthatchUserHandle: NuthatchUserHandle] = {
initializes fileName as the current log stream, userName as the current user, and RefIDType as the current ID type for voice messages. If can''t open the log file, or have some other kind of alpine failure, report back through some returned variable. On further calls to Nuthatch routines, if log can't be talked to then do what? For an abort, do a retry, for a failure to communicate, report back to walnut window that communication with log has failed and nuthatch functions may not be available. Make OpenLogFile into internal, non-entry proc, since the monitor is already locked. --
initializes fileName as the current log stream, userName as the current user, and RefIDType as the current ID type for voice messages.
ENABLE UNWIND => NULL;
logReadPoint: INT←-1;
Hack! Hack!--
updateProcess←NIL;
defaultType←"simple";
check out the database --
IF currentNUH#NIL THEN RETURN[TRUE, currentNUH];
[success, logReadPoint] ← NuthatchDB.InitializeDB[close: close];
IF ~success THEN RETURN;
nuthatchUserHandle←NEW[NuthatchUserRec←[]];
nuthatchUserHandle.userName←userName;
NuthatchLog.InitializeLog[nuthatchUserHandle];
nuthatchUserHandle.updateProcess←NIL;
nuthatchUserHandle.refIDType←RefIDType;
nuthatchUserHandle.logReadPoint←logReadPoint;
Hack! Hack!--
currentNUH←nuthatchUserHandle;
};
CatalogVoiceFile: PUBLIC PROC[
nuthatchUserHandle: NuthatchUserHandle,
creator: GVBasics.RName, get this from current user name --
voiceFileID: Rope.ROPE,
tuneNumber: INT,
recordedTime: BasicTime.GMT,
referenceCount: INT, 
samples: INT,    
startingSample: INT,  
encryptionKey: EncryptionKey←Thrush.nullKey,  
time: BasicTime.GMT,
type: Rope.ROPE
] = TRUSTED {
Writes a log entry to insert or update an entry for this voice file in the directory.
keyRope: Rope.ROPE;
cardKey: LONG POINTER TO ARRAY[0..2) OF LONG CARDINAL=LOOPHOLE [LONG[@encryptionKey]];
keyRope←IO.PutFR["%bB %bB", IO.card[cardKey[0]], IO.card[cardKey[1]]];
build log entry into a rope.--
pass it to NuthatchLog.WriteLogEntry[logRope, nuthatchUserHandle];--
NuthatchLog.WriteLogEntry[Rope.Concat[
IO.PutFR ["Catalog %g\n%g %d %g\n%d ", IO.rope[voiceFileID], IO.rope[nuthatchUserHandle.userName], IO.int[tuneNumber], IO.time[recordedTime], IO.int[referenceCount]],
IO.PutFR["%d %d %g %g %g\n", IO.int[samples], IO.int[startingSample], IO.rope[keyRope], IO.rope[type], IO.time[time]]], nuthatchUserHandle];
update DB from log --
NuthatchUpdater.DoLogEntries[nuthatchUserHandle]; 
};
GetDirectoryEntry: PUBLIC PROC[voiceFileID: VoiceFileID] RETURNS[
tuneNumber: Tune,
recordTime: BasicTime.GMT,
creator: GVBasics.RName,
samples: INT,
startSample: INT,
expirationDate: BasicTime.GMT,
encryptionKey: EncryptionKey,
type: Rope.ROPE,
referenceCount: INT,
found: BOOL] = TRUSTED {
Should catch DB problem here?
Reads the directory entry for this voice file.
[tuneNumber, recordTime, creator, samples, startSample, expirationDate, encryptionKey, type, referenceCount, found] ←
NuthatchDB.GetVoiceFileEntry[voiceFileID];
};
GetFileID: PUBLIC PROC[
ID: Rope.ROPE, nuthatchUserHandle: NuthatchUserHandle]
RETURNS [voiceFileID: VoiceFileID] = {
Given the user's ID for a voice message, return its voiceFileID.
Get IDTYpe and user's name from handle.
refIDType: Rope.ROPE ← nuthatchUserHandle.refIDType;
user: GVBasics.RName ← nuthatchUserHandle.userName;
Call nuthatchDB.lookup of some kind, giving user, IDType, and ID, to get voiceFileID
voiceFileID ← NuthatchDB.GetVoiceFileID[user, refIDType, ID];
};
MakeInterestEntry: PUBLIC PROC[
nuthatchUserHandle: NuthatchUserHandle,
voiceFileID: VoiceFileID←NIL,
refID: Rope.ROPENIL,
time: BasicTime.GMT] = {
Writes a log entry to create an entry in the InterestList relation for this voice file, associating the user, RefID, and RefIDType with the voiceMessageID.
SHOULD TEST FOR NO INPUT PARMS = NIL
logEntryRope:Rope.ROPE;
refIDType: Rope.ROPE←nuthatchUserHandle.refIDType;
user: GVBasics.RName←nuthatchUserHandle.userName;
time ← NHTime[time];
Put user's refID at end of line to make parsing easier
logEntryRope ← IO.PutFR["Create %g\n%g %g\n%g %g\n", IO.rope[voiceFileID], IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]];
NuthatchLog.WriteLogEntry[logEntryRope, nuthatchUserHandle];
NuthatchUpdater.DoLogEntries[nuthatchUserHandle];
};
AddInterest: PUBLIC PROC[
nuthatchUserHandle: NuthatchUserHandle,
voiceFileID: Rope.ROPENIL, --optional
refID: Rope.ROPENIL,
time: BasicTime.GMT] = {
Writes a log entry to increment the reference count for this voice file
logEntryRope: Rope.ROPE;
From the nuthatchUserHandle, get the user and the refIDType.
If voice file id is given, write one kind of entry; otherwise write other kind.
refIDType: Rope.ROPE←nuthatchUserHandle.refIDType;
user: GVBasics.RName←nuthatchUserHandle.userName;
time ← NHTime[time];
Put user's refID at end of line to make parsing easier
IF voiceFileID#NIL THEN
logEntryRope←IO.PutFR["AddRef %g\n%g %g\n%g %g\n", IO.rope[voiceFileID],
IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]]
ELSE {
voiceFileID ← NuthatchDB.GetVoiceFileID[
user, refIDType, refID, FALSE];
IF voiceFileID=NIL THEN { NuthatchDB.CloseTransaction[]; RETURN; };
logEntryRope←IO.PutFR["AddUserRef %g %g\n%g %g\n", IO.rope[user],
IO.time[time], IO.rope[refIDType], IO.rope[refID]];
};
NuthatchLog.WriteLogEntry[logEntryRope, nuthatchUserHandle];
NuthatchUpdater.DoLogEntries[nuthatchUserHandle]; 
};
LoseInterest: PUBLIC PROC[
nuthatchUserHandle: NuthatchUserHandle,
voiceFileID: Rope.ROPE, --optional --
refID: Rope.ROPE,
time: BasicTime.GMT] = {
Writes a log entry to decrement the reference count for this voice file --
success: BOOLFALSE;
refIDType: Rope.ROPE←nuthatchUserHandle.refIDType;
user: GVBasics.RName←nuthatchUserHandle.userName;
logEntryRope: Rope.ROPE;
time ← NHTime[time];
put user's refID at end of line to make parsing easier --
IF voiceFileID#NIL THEN
logEntryRope←IO.PutFR["LoseRef %g\n%g %g\n%g %g\n", IO.rope[voiceFileID],
IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]]
ELSE {
voiceFileID ← NuthatchDB.GetVoiceFileID[
user, refIDType, refID, FALSE];
IF voiceFileID=NIL THEN { NuthatchDB.CloseTransaction[]; RETURN; };
logEntryRope←IO.PutFR["LoseUserRef %g %g\n%g %g\n",
IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]];
};
NuthatchLog.WriteLogEntry[logEntryRope, nuthatchUserHandle];
NuthatchUpdater.DoLogEntries[nuthatchUserHandle]; 
};
RemoveInterestEntry: PUBLIC PROC[
nuthatchUserHandle: NuthatchUserHandle,
voiceFileID: Rope.ROPE, --optional --
refID: Rope.ROPE,
time: BasicTime.GMT] = {
Writes a log entry to decrement the reference count for this voice file. If the database has no record of this message containing voice, that's OK; just ignore it. (Happens after doing an expunge in Walnut, for example.) --
success: BOOLFALSE;
refIDType: Rope.ROPE←nuthatchUserHandle.refIDType;
user: GVBasics.RName←nuthatchUserHandle.userName;
logEntryRope: Rope.ROPE;
time ← NHTime[time];
IF voiceFileID#NIL THEN
logEntryRope←IO.PutFR["RemoveRef %g\n%g %g\n%g %g\n", IO.rope[voiceFileID],
IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]]
ELSE {
voiceFileID ← NuthatchDB.GetVoiceFileID[
user, refIDType, refID, FALSE];
IF voiceFileID=NIL THEN { NuthatchDB.CloseTransaction[]; RETURN; };
logEntryRope←IO.PutFR["RemoveUserRef %g %g\n%g %g\n",
IO.rope[user], IO.time[time], IO.rope[refIDType], IO.rope[refID]];
};
NuthatchLog.WriteLogEntry[logEntryRope, nuthatchUserHandle];
NuthatchUpdater.DoLogEntries[nuthatchUserHandle]; 
};
GetTune: PUBLIC PROC[voiceFileID: VoiceFileID] RETURNS [tune: Tune] = {
Given the voiceFileID for a voice message, return its tune number.
tuneStream: IO.STREAMIO.RIS[voiceFileID];
tune ← IO.GetInt[tuneStream];
IO.Close[tuneStream];
}; 
NHTime: PUBLIC PROC[time: BasicTime.GMT] RETURNS [nhTime: BasicTime.GMT] = {
RETURN[IF time#BasicTime.nullGMT THEN time ELSE BasicTime.Now[]]; };
SetSysNoise: Commander.CommandProc = {
Enter the voice file ID (second argument) under current profile ThrushServerInstance, time is now, type is $SysNoises, value is first argument, interest is TRUE!!
nuh: NuthatchUserHandle ← NEW[Nuthatch.NuthatchUserRec ← currentNUH^];
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
IF argv.argc#3 THEN RETURN[$Failure, "Args must be Noise-ID and VoiceFileID"];
nuh.userName ← UserProfile.Token[key: "ThrushServerInstance", default: "Morley.Lark"];
nuh.refIDType ← "SysNoises";
AddInterest[nuh, argv[2], argv[1], BasicTime.nullGMT];
currentNUH.logReadPoint ← nuh.logReadPoint;
nuh←NIL;
};
DoLog: Commander.CommandProc = {
NuthatchUpdater.DoLogEntries[currentNUH];
};
AllLog: Commander.CommandProc = {
currentNUH.logReadPoint ← 0;
NuthatchUpdater.DoLogEntries[currentNUH];
};
ViewCmd: Commander.CommandProc = TRUSTED {
Nice.View[pd, "Nuthatch PD"];
};
Commander.Register["VuNuthatch", ViewCmd, "Program Management variables for Nuthatch"];
Commander.Register["DoLog", DoLog, "Make sure all Nuthatch log entries have been processed."];
Commander.Register["AllLog", AllLog, "Play Nuthatch log from the beginning."];
Commander.Register["SysNoise", SetSysNoise, "SysNoise Rollback \"12 3-Mar-83 4:52:07 PDT\" -- makes the entry in Nuthatch DB"];
}.