<> <> <<(Simplified substantially to handle only those tools that are considered part of the current release)>> <<(Changed ticksToWait to 60 -- closes transactions more rapidly now)>> <> <> <<>> DIRECTORY Atom USING [MakeAtom, GetPName], Commander, CommandTool, DBTools, FS, Booting USING [CheckpointProc, RegisterProcs, RollbackProc], Convert USING[RopeFromRope], DB, IO, IOClasses, TextFind, MessageWindow USING [Append], Process USING[Ticks, SecondsToTicks, Detach, Pause], Rope USING [Cat, Equal, Substr, ROPE, Length, Concat], UserProfile USING [Token, ProfileChangedProc, CallWhenProfileChanges], UserCredentials USING[CredentialsChangeProc], ViewerClasses ; DBToolsImpl: CEDAR MONITOR IMPORTS Atom, Booting, CommandTool, Convert, FS, IO, IOClasses, MessageWindow, Process, Rope, DB, TextFind, UserProfile EXPORTS DBTools = BEGIN OPEN DB, IO, Rope; <> ROPE: TYPE = Rope.ROPE; <> toolDomain: DB.Domain; -- the domain of tools <> loadFileProp: [0..6) = 0; -- the .load file for the tool docFileProp: [0..6) = 1; -- the documentation file for the tool briefDocProp: [0..6) = 2; -- a brief description of the tool viewerFlavorProp: [0..6) = 3; -- the registered viewer flavor for the tool namePatternProp: [0..6) = 4; -- the name pattern for the tool argumentPatternProp: [0..6) = 5; -- the argument pattern for the tool PropertyRecord: TYPE = RECORD[name: ROPE, prop: DB.Attribute _ NIL]; propertyTable: ARRAY[0..6) OF PropertyRecord _ [[name: "LoadFile"], [name: "DocFile"], [name: "BriefDoc"], [name: "ViewerFlavor"], [name: "NamePattern"], [name: "ArgumentPattern"]]; -- a list consisting of all of the properties given above flavorRelation: DB.Relation; -- the relation corresponding to the flavor property flavorOf: DB.Attribute; -- the "of" attribute of the flavor relation (the "is" attribute is the viewerFlavorProp) descriptorRelation: DB.Relation; -- the description of tools descriptorOf: DB.Attribute; -- a tool descriptorIs: DB.Attribute; -- is a ROPE ToolDB: PUBLIC ROPE _ NIL; pendingChange: BOOL _ FALSE; -- this is set by the rollback and credentials change procs to remember a potential change of state; it is checked by CarefullyApply, which will call ResetSchema if it is true toolTrans: DB.Transaction _ NIL; readOnly: PUBLIC BOOLEAN _ TRUE; -- true if the segment is readonly activity: BOOLEAN _ FALSE; -- true if a database operation has been performed recently ticksToWait: Process.Ticks _ Process.SecondsToTicks[60]; transOpened: CONDITION; LoadError: ERROR = CODE; loadedList: LIST OF ROPE _ NIL; -- a list of all of the loads performed <> EstablishToolDB: PUBLIC ENTRY PROC [file: Rope.ROPE _ NIL] = { ENABLE UNWIND => NULL; ToolDB _ IF file = NIL THEN UserProfile.Token[key: "Tool.Segment", default: "[Luther.Alpine]Tool.Segment"] ELSE file; pendingChange _ TRUE }; WatchDBActivity: PROC[] = { WHILE TRUE DO Process.Pause[ticksToWait]; CheckConnection[] ENDLOOP }; CheckConnection: ENTRY PROC[] = { ENABLE UNWIND => NULL; IF NOT activity THEN { CloseTransaction[]; -- don't keep the connection open too long WAIT transOpened }; activity _ FALSE; }; Close: PUBLIC ENTRY PROC [] = { CloseTransaction[] }; CloseTransaction: INTERNAL PROC [] = { caughtAborted: BOOL _ FALSE; IF toolTrans # NIL THEN DB.CloseTransaction[toolTrans ! DB.Aborted => { caughtAborted _ TRUE; CONTINUE }]; IF caughtAborted THEN DB.AbortTransaction[toolTrans]; toolTrans _ NIL }; <> RegisterTool: PUBLIC ENTRY PROC [toolName, loadFile: ROPE] ~ { ENABLE UNWIND => NULL; IF InternalRegisterTool[toolName: toolName, loadFile: loadFile] THEN DB.MarkTransaction[toolTrans] ELSE MessageWindow.Append["Can not register tool."]}; InternalRegisterTool: INTERNAL PROC[toolName: ROPE, loadFile, docFile, briefDoc: ROPE _ NIL, descriptors: LIST OF ROPE _ NIL] RETURNS[success: BOOLEAN] = { ENABLE UNWIND => NULL; DoRegisterTool: PROC[] = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; IF loadFile # NIL THEN [] _ SetP[toolEntity, propertyTable[loadFileProp].prop, S2V[loadFile]]; IF docFile # NIL THEN [] _ SetP[toolEntity, propertyTable[docFileProp].prop, S2V[docFile]]; IF briefDoc # NIL THEN [] _ SetP[toolEntity, propertyTable[briefDocProp].prop, S2V[briefDoc]]; <> IF descriptors = NIL THEN RETURN; BEGIN <> toolAttrValue: DB.AttributeValue = [descriptorOf, toolEntity]; oldrships: RelshipSet = RelationSubset[descriptorRelation, LIST[toolAttrValue]]; FOR old: DB.Relship _ NextRelship[oldrships], NextRelship[oldrships] UNTIL old = NIL DO DB.DestroyRelship[old] ENDLOOP; DB.ReleaseRelshipSet[oldrships]; <> FOR ds: LIST OF ROPE _ descriptors, ds.rest WHILE ds # NIL DO newDescrValue: DB.AttributeValue = [descriptorIs, S2V[descriptors.first]]; [] _ DeclareRelship[descriptorRelation, LIST[toolAttrValue, newDescrValue]] ENDLOOP END }; IF readOnly OR ToolDB = NIL OR toolName = NIL THEN { IF readOnly THEN MessageWindow.Append["File is read only."] ELSE IF ToolDB = NIL THEN MessageWindow.Append["ToolDB is NIL."] ELSE MessageWindow.Append["toolName is NIL."]; RETURN; -- nothing to do }; success _ CarefullyApply[DoRegisterTool] }; GetLoadFile: PUBLIC ENTRY PROC [toolName: ROPE] RETURNS [loadFile: ROPE] ~ { ENABLE UNWIND => NULL; [loadFile ~ loadFile] _ InternalGetToolProps[toolName] }; InternalGetToolProps: INTERNAL PROC [toolName: ROPE] RETURNS [loadFile, docFile, briefDoc: ROPE] ~ { ENABLE UNWIND => NULL; DoGetToolInfo: PROC[] = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; toolAttrValue: DB.AttributeValue = [descriptorOf, toolEntity]; IF toolEntity = NIL THEN RETURN; loadFile _ V2S[GetP[toolEntity, propertyTable[loadFileProp].prop]]; docFile _ V2S[GetP[toolEntity, propertyTable[docFileProp].prop]]; briefDoc _ V2S[GetP[toolEntity, propertyTable[briefDocProp].prop]] }; IF NOT CarefullyApply[DoGetToolInfo] THEN RETURN[NIL, NIL, NIL] }; GetToolDocumentation: PUBLIC ENTRY PROC [toolName: ROPE] RETURNS [docFile, briefDoc: ROPE] ~ { ENABLE UNWIND => NULL; [docFile ~ docFile, briefDoc ~ briefDoc] _ InternalGetToolProps[toolName] }; SetToolDocumentation: PUBLIC ENTRY PROC [toolName: ROPE, docFile, briefDoc: ROPE _ NIL] ~ { ENABLE UNWIND => NULL; IF InternalRegisterTool[toolName: toolName, docFile: docFile, briefDoc: briefDoc] THEN DB.MarkTransaction[toolTrans] }; SetToolDescriptors: PUBLIC ENTRY PROC[toolName: ROPE, descriptors: LIST OF ROPE _ NIL] = { ENABLE UNWIND => NULL; DoSetDescriptors: PROC[] = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; IF descriptors = NIL THEN RETURN; BEGIN <> toolAttrValue: DB.AttributeValue = [descriptorOf, toolEntity]; oldrships: RelshipSet = RelationSubset[descriptorRelation, LIST[toolAttrValue]]; FOR old: DB.Relship _ NextRelship[oldrships], NextRelship[oldrships] UNTIL old = NIL DO DB.DestroyRelship[old] ENDLOOP; DB.ReleaseRelshipSet[oldrships]; <> FOR ds: LIST OF ROPE _ descriptors, ds.rest WHILE ds # NIL DO newDescrValue: DB.AttributeValue = [descriptorIs, S2V[ds.first]]; [] _ DeclareRelship[descriptorRelation, LIST[toolAttrValue, newDescrValue]] ENDLOOP END }; IF readOnly OR ToolDB = NIL OR toolName = NIL THEN RETURN; -- nothing to do IF CarefullyApply[DoSetDescriptors] THEN DB.MarkTransaction[toolTrans] }; <<>> GetToolDescriptors: PUBLIC ENTRY PROC[toolName: ROPE] RETURNS[descriptors: LIST OF ROPE] = { DoGetToolDescriptors: PROC[] = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; toolAttrValue: DB.AttributeValue = [descriptorOf, toolEntity]; IF toolEntity = NIL THEN RETURN; BEGIN toolAttrValue: DB.AttributeValue = [descriptorOf, toolEntity]; rships: RelshipSet = RelationSubset[descriptorRelation, LIST[toolAttrValue]]; FOR old: DB.Relship _ NextRelship[rships], NextRelship[rships] UNTIL old = NIL DO descriptors _ CONS[V2S[DB.GetF[old, descriptorIs]], descriptors] ENDLOOP; DB.ReleaseRelshipSet[rships]; END }; IF NOT CarefullyApply[DoGetToolDescriptors] THEN RETURN[NIL] }; <<>> FindMatchingTools: PUBLIC ENTRY PROC [descriptors: LIST OF ROPE] RETURNS [tools: LIST OF ROPE] ~ { ENABLE UNWIND => NULL; DoFindMatch: PROC[] = { entityList, newList: LIST OF DB.Entity _ NIL; descriptorList: LIST OF DB.AttributeValue _ NIL; rshipSet: DB.RelshipSet; IF descriptors = NIL THEN { -- produce the list of all of the tools in the database toolList: LIST OF DB.Entity = DB.EntitySetToList[DB.DomainSubset[toolDomain]]; FOR tl: LIST OF DB.Entity _ toolList, tl.rest UNTIL tl = NIL DO tools _ CONS[DB.NameOf[tl.first], tools] ENDLOOP; RETURN }; <> FOR desc: LIST OF ROPE _ descriptors, desc.rest UNTIL desc = NIL DO descriptorList _ CONS[AttributeValue[descriptorIs, S2V[desc.first]], descriptorList] ENDLOOP; <> rshipSet _ RelationSubset[ descriptorRelation, LIST[descriptorList.first]]; descriptorList _ descriptorList.rest; FOR rs: DB.Relship _ NextRelship[rshipSet], NextRelship[rshipSet] UNTIL rs = NIL DO entityList _ CONS[V2E[GetF[rs, descriptorIs]], entityList] ENDLOOP; ReleaseRelshipSet[rshipSet]; <> FOR desc: LIST OF ROPE _ descriptors, desc.rest UNTIL desc = NIL DO descrAttrValue: DB.AttributeValue = [descriptorIs, desc.first]; FOR eL: LIST OF DB.Entity _ entityList, eL.rest UNTIL eL = NIL DO toolAttrValue: DB.AttributeValue = [descriptorOf, eL.first]; IF DeclareRelship[descriptorRelation, LIST[toolAttrValue, descrAttrValue], OldOnly] # NIL THEN newList _ CONS[eL.first, newList] ENDLOOP; entityList _ newList; newList _ NIL ENDLOOP; <> FOR eL: LIST OF Entity _ entityList, eL.rest UNTIL eL = NIL DO tools _ CONS[DB.NameOf[eL.first], tools] ENDLOOP }; IF NOT CarefullyApply[DoFindMatch] THEN RETURN[NIL] }; InternalLoadTool: INTERNAL PROC [toolName: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] ~ { loadFile: ROPE = InternalGetToolProps[toolName].loadFile; command: ROPE; IF loadFile = NIL THEN RETURN; command _ Rope.Cat["Source ///Commands/", loadFile, "\n"]; InternalDoLoad[toolName, command, errorStream]; }; InternalDoLoad: INTERNAL PROC[toolName, commandLine: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] = { DoCommand[commandLine, errorStream]; InternalAddLoaded[toolName] }; LoadTool: PUBLIC ENTRY PROC [toolName: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] ~ { ENABLE UNWIND => NULL; IF NOT CheckLoaded[toolName] THEN InternalLoadTool[toolName, errorStream] }; AddLoaded: ENTRY PROC [tool: ROPE] ~ { InternalAddLoaded[tool] }; InternalAddLoaded: INTERNAL PROC [tool: ROPE] ~ { loadedList _ CONS[tool, loadedList] }; CheckLoaded: PROC [tool: ROPE] RETURNS [yes: BOOLEAN] ~ { yes _ FALSE; FOR loaded: LIST OF ROPE _ loadedList, loaded.rest UNTIL loaded = NIL DO IF Rope.Equal[loaded.first, tool] THEN RETURN[TRUE] ENDLOOP }; ApplyTool: PUBLIC ENTRY PROC [toolName, arguments: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] ~ { loadFile: ROPE = InternalGetToolProps[toolName].loadFile; command: ROPE; IF Rope.Equal[loadFile, ""] AND DB.DeclareEntity[toolDomain, toolName, OldOnly] = NIL THEN RETURN; -- if these are true, then the tool isn't even registered (it may not have a load file if the tools is part of LifeSupport) IF NOT Rope.Equal[loadFile, ""] THEN { -- load the .load file from ///Commands InternalLoadTool[toolName, errorStream]; command _ Rope.Concat["///Commands/", toolName] } ELSE command _ toolName; IF arguments # NIL THEN command _ Rope.Cat[command, " ", arguments, "\n"] ELSE command _ Rope.Concat[command, "\n"]; TRUSTED {Process.Detach[ FORK DoCommand[command, errorStream] ] } }; DoCommand: PROC[commandLine: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] = { cmdHandle: Commander.Handle = NEW[Commander.CommandObject _ []]; cmdHandle.err _ errorStream; cmdHandle.out _ IO.noWhereStream; [] _ CommandTool.DoCommandRope[commandLine: commandLine, parent: cmdHandle] }; RegisterViewerFlavor: PUBLIC ENTRY PROC [tool: ROPE, flavor: ViewerClasses.ViewerFlavor] ~ { ENABLE UNWIND => NULL; DoRegisterViewerFlavor: PROC = { toolEntity: DB.Entity = DeclareEntity[toolDomain, tool, OldOnly]; atomName: Rope.ROPE = IF flavor = NIL THEN NIL ELSE Atom.GetPName[flavor]; IF toolEntity = NIL THEN RETURN; [] _ SetP[toolEntity, propertyTable[viewerFlavorProp].prop, S2V[atomName]] }; IF CarefullyApply[DoRegisterViewerFlavor] THEN DB.MarkTransaction[toolTrans] }; RegisterNamePattern: PUBLIC ENTRY PROC [toolName: ROPE, pattern: ROPE] ~ { ENABLE UNWIND => NULL; DoRegisterNamePattern: PROC = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName, OldOnly]; IF toolEntity = NIL THEN RETURN; [] _ SetP[toolEntity, propertyTable[namePatternProp].prop, S2V[pattern]] }; IF CarefullyApply[DoRegisterNamePattern] THEN DB.MarkTransaction[toolTrans] }; RegisterArgumentPattern: PUBLIC ENTRY PROC [toolName: ROPE, pattern: ROPE] ~ { ENABLE UNWIND => NULL; DoRegisterArgumentPattern: PROC = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName, OldOnly]; IF toolEntity = NIL THEN RETURN; [] _ SetP[toolEntity, propertyTable[argumentPatternProp].prop, S2V[pattern]] }; IF CarefullyApply[DoRegisterArgumentPattern] THEN DB.MarkTransaction[toolTrans] }; GetViewerInfo: PUBLIC ENTRY PROC [toolName: ROPE] RETURNS [flavor: ViewerClasses.ViewerFlavor, namePattern, argumentPattern: ROPE] ~ { ENABLE UNWIND => NULL; [flavor, namePattern, argumentPattern] _ InternalGetViewerInfo[toolName] }; InternalGetViewerInfo: INTERNAL PROC [toolName: ROPE] RETURNS [flavor: ViewerClasses.ViewerFlavor, namePattern, argumentPattern: ROPE] ~ { ENABLE UNWIND => NULL; DoGetToolInfo: PROC[] = { toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; flavor _ Atom.MakeAtom[V2S[GetP[toolEntity, propertyTable[viewerFlavorProp].prop]]]; namePattern _ V2S[GetP[toolEntity, propertyTable[namePatternProp].prop]]; argumentPattern _ V2S[GetP[toolEntity, propertyTable[argumentPatternProp].prop]] }; IF NOT CarefullyApply[DoGetToolInfo] THEN RETURN[NIL, NIL, NIL] }; ViewerToTool: PUBLIC ENTRY PROC [v: ViewerClasses.Viewer] RETURNS [toolName: ROPE, arguments: ROPE] ~ { ENABLE UNWIND => NULL; DoViewerTool: PROC = { viewerName: ROPE = v.name; flavor: ROPE = Atom.GetPName[v.class.flavor]; flavorAttrValue: DB.AttributeValue = [propertyTable[viewerFlavorProp].prop, S2V[flavor]]; toolSet: DB.RelshipSet = RelationSubset[flavorRelation, LIST[flavorAttrValue]]; toolRelation: DB.Relship _ NextRelship[toolSet]; toolEntity: DB.Entity _ IF toolRelation = NIL THEN NIL ELSE V2E[GetF[toolRelation, flavorOf]]; ReleaseRelshipSet[toolSet]; IF toolEntity = NIL THEN BEGIN toolSet: DB.EntitySet = DomainSubset[toolDomain]; FOR tool: DB.Entity _ NextEntity[toolSet], NextEntity[toolSet] UNTIL tool = NIL DO namePattern: ROPE = V2S[GetP[tool, propertyTable[namePatternProp].prop]]; pattern: TextFind.Finder = TextFind.CreateFromRope[namePattern]; found: BOOLEAN; after, before: TextFind.Offset; [found ~ found, after ~ after, before ~ before] _ TextFind.SearchRope[pattern, viewerName]; IF found AND (before = 0) AND (after = viewerName.Length[]) THEN {toolEntity _ tool; EXIT} ENDLOOP; ReleaseEntitySet[toolSet]; IF toolEntity = NIL THEN { toolName _ NIL; arguments _ NIL; RETURN } END; BEGIN argPattern: ROPE = V2S[GetP[toolEntity, propertyTable[argumentPatternProp].prop]]; pattern: TextFind.Finder = TextFind.CreateFromRope[argPattern]; found: BOOLEAN; at, atEnd: TextFind.Offset; [found ~ found, at ~ at, atEnd ~ atEnd] _ TextFind.SearchRope[pattern, viewerName]; IF found THEN arguments _ Rope.Substr[viewerName, at, atEnd-at]; toolName _ DB.NameOf[toolEntity] END }; IF NOT CarefullyApply[DoViewerTool] THEN RETURN[NIL, NIL] }; <> stream: IO.STREAM; -- the stream used either by Parse to read a catalogue or by WriteCatalogue to print one Parse: INTERNAL PROCEDURE[] = { ENABLE IO.EndOfStream, IO.Error => TRUSTED{ ERROR LoadError }; DO toolName: ROPE = IO.GetTokenRope[stream ! IO.EndOfStream => GOTO Done].token; toolEntity: DB.Entity = DeclareEntity[toolDomain, toolName]; ofValue: DB.AttributeValue = [descriptorOf, toolEntity]; FOR i: [0..6) IN [0..6) DO propertyName: Rope.ROPE = stream.GetID[]; propertyValue: Rope.ROPE = stream.GetRopeLiteral[]; FOR j: [0..6) IN [0..6) DO IF Rope.Equal[propertyTable[j].name, propertyName] THEN {[] _ DB.SetP[toolEntity, propertyTable[j].prop, S2V[propertyValue]]; EXIT} ENDLOOP; ENDLOOP; IF NOT Rope.Equal[stream.GetTokenRope[].token, "("] THEN ERROR LoadError; DO token: ROPE; token _ IO.GetCedarTokenRope[stream].token; IF Rope.Equal[token,")"] THEN EXIT; [] _ DeclareRelship[descriptorRelation, LIST[ofValue, AttributeValue[descriptorIs, S2V[token]]]] ENDLOOP ENDLOOP; EXITS Done => stream.Close[]; }; InternalWrite: INTERNAL PROC[] = { tools: DB.EntitySet = DB.DomainSubset[toolDomain]; FOR tool: DB.Entity _ NextEntity[tools], NextEntity[tools] UNTIL tool = NIL DO OPEN IO; stream.PutF["\n %g", rope[DB.NameOf[tool]]]; FOR i: [0..6) IN [0..6) DO stream.PutF["\n %g %g ", rope[propertyTable[i].name], rope[Convert.RopeFromRope[V2S[GetP[tool, propertyTable[i].prop]]]]] ENDLOOP; BEGIN descrAttrValue: DB.AttributeValue = [descriptorOf, tool]; descRships: DB.RelshipSet = DB.RelationSubset[descriptorRelation, LIST[descrAttrValue]]; stream.Put[rope[" ( "]]; FOR descr: DB.Relship _ NextRelship[descRships], NextRelship[descRships] UNTIL descr = NIL DO descriptor: ROPE = V2S[GetF[descr, descriptorIs]]; stream.PutF[" %g", rope[Convert.RopeFromRope[descriptor]]] ENDLOOP; stream.Put[rope[" ) "]]; DB.ReleaseRelshipSet[descRships] END ENDLOOP; stream.Close[] }; WriteCatalogue: PUBLIC ENTRY PROC [file: Rope.ROPE] = { ENABLE UNWIND => NULL; stream _ FS.StreamOpen[file, $append ! FS.Error => { MessageWindow.Append[error.explanation]; stream _ NIL; CONTINUE}]; IF stream # NIL THEN [] _ CarefullyApply[InternalWrite] }; <> OpenUp: Booting.RollbackProc = { DB.Initialize[nCachePages: 256] }; CloseTrans: ENTRY Booting.CheckpointProc = { CloseTransaction[] }; NewUserReset: ENTRY UserCredentials.CredentialsChangeProc = { CloseTransaction[] }; ProfileChangeReset: ENTRY UserProfile.ProfileChangedProc = { newToolDB: ROPE = UserProfile.Token[key: "Tool.Segment", default: "[Luther.Alpine]Tool.segment"]; IF NOT Rope.Equal[ToolDB, newToolDB] THEN { <> ToolDB _ newToolDB; pendingChange _ TRUE } }; ResetSchema: INTERNAL PROC[changingDBs: BOOL] = { IF pendingChange THEN CloseTransaction[]; IF toolTrans # NIL THEN RETURN; IF NOT SetUpSegment[] THEN RETURN; IF NOT DB.Null[toolDomain] THEN RETURN; -- all is well, don't bother recomputing schema toolDomain _ DeclareDomain["Tool", $Tool]; FOR i: [0..6) IN [0..6) DO propertyTable[i].prop _ DeclareProperty[relationName: propertyTable[i].name, of: toolDomain, is: RopeType, segment: $Tool] ENDLOOP; flavorRelation _ V2E[GetP[propertyTable[viewerFlavorProp].prop, aRelationIs, aRelationOf]]; flavorOf _ DB.DeclareAttribute[r: flavorRelation, name: "of", version: OldOnly]; descriptorRelation _ DeclareRelation[name: "Descriptor", segment: $Tool]; descriptorOf _ DeclareAttribute[r: descriptorRelation, name: "of", type: toolDomain]; descriptorIs _ DeclareAttribute[r: descriptorRelation, name: "is", type: RopeType]; toolTrans _ DB.TransactionOf[$Tool] }; ReadCatalogue: PUBLIC ENTRY PROC[file: Rope.ROPE] = TRUSTED { ENABLE BEGIN UNWIND => NULL; LoadError => GOTO Failure; END; stream _ FS.StreamOpen[file ! FS.Error => {stream _ NIL; CONTINUE}]; IF stream = NIL THEN RETURN; stream _ IOClasses.CreateCommentFilterStream[stream]; DB.MarkTransaction[toolTrans]; IF CarefullyApply[Parse] THEN DB.MarkTransaction[toolTrans] ELSE DB.AbortTransaction[toolTrans] EXITS Failure => DB.AbortTransaction[toolTrans]; }; CarefullyApply: INTERNAL PROC [proc: PROC[]] RETURNS [succeeded: BOOL] ~ { ENABLE DB.Error, DB.Failure, DB.Aborted => {succeeded _ FALSE; GOTO Quit}; <> ResetSchema[changingDBs: pendingChange]; pendingChange _ FALSE; activity _ TRUE; succeeded _ TRUE; proc[ ! DB.Aborted => { succeeded _ FALSE; CONTINUE } ]; IF succeeded THEN RETURN; -- no aborted occurred DB.AbortTransaction[toolTrans]; toolTrans _ NIL; -- there isn't any transaction anymore ResetSchema[changingDBs: FALSE]; proc[]; -- don't bother trying to restart here -- succeeded _ TRUE; EXITS Quit => NULL; }; SetUpSegment: INTERNAL PROC[] RETURNS [success: BOOL] ~ { ENABLE DB.Aborted, DB.Failure, DB.Error => {success _ FALSE; CONTINUE}; segment: ATOM = $Tool; segmentNumber: NAT = 320B; readOnly _ FALSE; success _ TRUE; DB.Initialize[nCachePages: 256]; DB.DeclareSegment[ToolDB, segment, segmentNumber, FALSE]; DB.OpenTransaction[segment ! DB.Error => IF code = ProtectionViolation THEN {success _ FALSE; CONTINUE} ELSE REJECT ]; IF NOT success THEN { <> DB.CloseTransaction[DB.TransactionOf[segment]]; DB.DeclareSegment[ToolDB, segment, segmentNumber, TRUE, FALSE]; DB.OpenTransaction[segment]; success _ TRUE }; readOnly _ DB.GetSegmentInfo[segment].readOnly; toolTrans _ DB.GetSegmentInfo[segment].trans; NOTIFY transOpened -- start up the watch dog process again to try to shut it down }; <> TRUSTED { Booting.RegisterProcs[c: CloseTrans, r: OpenUp]; UserProfile.CallWhenProfileChanges[ProfileChangeReset]; EstablishToolDB[]; Process.Detach[ FORK WatchDBActivity[] ] }; END.