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.PD_NEW[Nuthatch.PD]; defaultType: PUBLIC Rope.ROPE; updateProcess: PUBLIC PROCESS; tune: PUBLIC Tune; samples: PUBLIC INT; startSample: PUBLIC INT; key: PUBLIC EncryptionKey; logStream: IO.STREAM_ NIL; LogEntryType: TYPE = {increment, decrement, update}; RefIDType: TYPE = IDType; RefID: TYPE = ID; InitializeNuthatch: PUBLIC PROC[ userName: GVBasics.RName_NIL, logFileName: Rope.ROPE_NIL, RefIDType: Rope.ROPE_NIL, close: BOOL_TRUE] RETURNS [success: BOOL_TRUE, nuthatchUserHandle: NuthatchUserHandle] = { ENABLE UNWIND => NULL; logReadPoint: INT_-1; updateProcess_NIL; defaultType_"simple"; 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; currentNUH_nuthatchUserHandle; }; CatalogVoiceFile: PUBLIC PROC[ nuthatchUserHandle: NuthatchUserHandle, 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 { 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]]]; 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]; 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 { [tuneNumber, recordTime, creator, samples, startSample, expirationDate, encryptionKey, type, referenceCount, found] _ NuthatchDB.GetVoiceFileEntry[voiceFileID]; }; GetFileID: PUBLIC PROC[ ID: Rope.ROPE, nuthatchUserHandle: NuthatchUserHandle] RETURNS [voiceFileID: VoiceFileID] = { refIDType: Rope.ROPE _ nuthatchUserHandle.refIDType; user: GVBasics.RName _ nuthatchUserHandle.userName; voiceFileID _ NuthatchDB.GetVoiceFileID[user, refIDType, ID]; }; MakeInterestEntry: PUBLIC PROC[ nuthatchUserHandle: NuthatchUserHandle, voiceFileID: VoiceFileID_NIL, refID: Rope.ROPE_NIL, time: BasicTime.GMT] = { logEntryRope:Rope.ROPE; refIDType: Rope.ROPE_nuthatchUserHandle.refIDType; user: GVBasics.RName_nuthatchUserHandle.userName; time _ NHTime[time]; 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.ROPE_NIL, --optional refID: Rope.ROPE_NIL, time: BasicTime.GMT] = { logEntryRope: Rope.ROPE; refIDType: Rope.ROPE_nuthatchUserHandle.refIDType; user: GVBasics.RName_nuthatchUserHandle.userName; time _ NHTime[time]; 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] = { success: BOOL_FALSE; refIDType: Rope.ROPE_nuthatchUserHandle.refIDType; user: GVBasics.RName_nuthatchUserHandle.userName; logEntryRope: Rope.ROPE; time _ NHTime[time]; 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] = { success: BOOL_FALSE; 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] = { tuneStream: IO.STREAM_ IO.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 = { 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]; }; Nice.View[pd, "Nuthatch PD"]; 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"]; }. ØFile: NuthatchImpl.mesa Last Edited by: Swinehart, April 30, 1984 4:57:22 pm PDT 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. Hack! Hack!-- check out the database -- Hack! Hack!-- creator: GVBasics.RName, get this from current user name -- Writes a log entry to insert or update an entry for this voice file in the directory. build log entry into a rope.-- pass it to NuthatchLog.WriteLogEntry[logRope, nuthatchUserHandle];-- update DB from log -- Should catch DB problem here? Reads the directory entry for this voice file. Given the user's ID for a voice message, return its voiceFileID. Get IDTYpe and user's name from handle. Call nuthatchDB.lookup of some kind, giving user, IDType, and ID, to get voiceFileID 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 Put user's refID at end of line to make parsing easier Writes a log entry to increment the reference count for this voice file From the nuthatchUserHandle, get the user and the refIDType. If voice file id is given, write one kind of entry; otherwise write other kind. Put user's refID at end of line to make parsing easier Writes a log entry to decrement the reference count for this voice file -- put user's refID at end of line to make parsing easier -- 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.) -- Given the voiceFileID for a voice message, return its tune number. Enter the voice file ID (second argument) under current profile ThrushServerInstance, time is now, type is $SysNoises, value is first argument, interest is TRUE!! Ê Í˜Jšœ™Jšœ8™8J˜šÏk ˜ Jšœ œ˜*Jšœ œ˜,Jšœ œ ˜Jšœ˜Jšœœ ˜J˜ Jšœ œE˜UJšœ œ ˜1Jšœœ˜%Jšœœ œ˜Jšœ œœ˜$Jšœœ ˜J˜Jšœ˜J˜—šœœ˜Jšœ$œE˜rJšœ ˜Jšœ ˜—˜Jšœ œ˜&Jš œœœ œœ œ˜,J˜Jšœ œœ˜Jšœœœ˜Jšœœ˜Jšœ œœ˜Jšœ œœ˜Jšœœ˜J˜J˜Jšœ œœœ˜Jšœœ"˜4Jšœ œ ˜Jšœœœ˜J˜—šÏnœœœ˜ Jšœœ˜Jšœœœ˜Jšœœ˜Jš œœœœ œœ-˜ZJšœÓ™ÓJ˜Jšœ†™†—˜Jšœœœ˜Jšœœ˜J˜Jšœ™Jšœœ˜J˜J˜Jšœ™Jš œ œœœœ˜0Jšœ@˜@Jšœ œœ˜Jšœœ˜+J˜%Jšœ.˜.Jšœ!œ˜%J˜'˜.J˜—Jšœ™J˜J˜J˜—šžœœœ˜J˜'Jšœ<™