<> <> <> <<(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, DottedPair, DottedPairNode], Commander, CommandTool, DBTools, FS, Booting USING [CheckpointProc, RegisterProcs, RollbackProc], Convert USING[RopeFromRope], DB, DBDefs, IO, IOClasses, TextFind, Process USING[Ticks, SecondsToTicks, Detach, Pause], Rope USING [Cat, Equal, Substr, ROPE, Concat], UserCredentials USING[CredentialsChangeProc, Get], ViewerClasses ; DBToolsImpl: CEDAR MONITOR IMPORTS Atom, Booting, Commander, CommandTool, Convert, FS, IO, IOClasses, Process, Rope, DB, TextFind, UserCredentials EXPORTS DBTools = BEGIN OPEN DB, DBDefs, Rope; <> ROPE: TYPE = Rope.ROPE; <> toolDomain: DBDefs.Domain; -- the domain of tools ToolTypeProc: PROC[] RETURNS[DBDefs.TypeCode] = { RETURN[DB.TypeForDomain[toolDomain]] }; ToolType: DB.TypeSpec = [indirect[ToolTypeProc]]; <<>> <> 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, relation: DBDefs.Relation _ NIL]; propertyTable: ARRAY[0..6) OF PropertyRecord _ [[name: "LoadFile"], [name: "DocFile"], [name: "BriefDoc"], [name: "ViewerFlavor"], [name: "NamePattern"], [name: "ArgumentPattern"]]; <> propertyValueField: DB.Field = [name: "is", type: DB.String, lengthHint: 30]; propertyKeyField: DB.Field _ [name: "of", type: ToolType]; propertyKeyAttribute: CARDINAL = 0; propertyValueAttribute: CARDINAL = 1; descriptorRelation: DBDefs.Relation; -- the description of tools <> descriptionField: DB.Field = [name: "is", type: DB.String, lengthHint: 100]; descriptorField: DB.Field _ [name: "of", type: ToolType]; descriptorIndex: Index; viewerFlavorIndex: Index; descriptorAttribute: CARDINAL = 0; descriptionAttribute: CARDINAL = 1; ToolDB: PUBLIC ROPE _ NIL; stopped: BOOL _ TRUE; stupidProperty: Atom.DottedPair = NEW[Atom.DottedPairNode _ [key: $DBToolCommandHandle, val: NIL]]; crockCmdHandle: Commander.Handle = NEW[Commander.CommandObject _ [propertyList: LIST[stupidProperty]]]; toolTrans: DB.TransactionHandle _ 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[5*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; defaultUserSearchRule: ROPE = Rope.Cat["///Users/", UserCredentials.Get[].name, "/Commands/"]; defaultSearchRules: LIST OF ROPE = LIST[defaultUserSearchRule, "///Commands/"]; <> CommandTool.AddSearchRule[crockCmdHandle, NIL]; -- throw previous ones away FOR rules: LIST OF ROPE _ defaultSearchRules, rules.rest UNTIL rules = NIL DO CommandTool.AddSearchRule[crockCmdHandle, rules.first] ENDLOOP; IF Rope.Equal[ToolDB, file] THEN RETURN; ToolDB _ file; stopped _ TRUE; -- protection against errors that might arise in the opening CloseTransaction[]; OpenDB[]; stopped _ FALSE }; 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 [] = { ENABLE UNWIND => NULL; CloseTransaction[]; stopped _ TRUE }; CloseTransaction: INTERNAL PROC [] = { caughtAborted: BOOL _ FALSE; IF toolTrans = NIL THEN RETURN; DB.CloseTransaction[toolTrans ! DB.Aborted => { caughtAborted _ TRUE; CONTINUE }; DB.Error, DB.Failure => CONTINUE ]; IF caughtAborted THEN DB.AbortTransaction[toolTrans]; toolTrans _ NIL }; OpenTransaction: INTERNAL PROC [] = { schemaInvalid: BOOL; IF toolTrans = NIL THEN { [toolTrans, schemaInvalid] _ DB.OpenTransaction[$Tool]; IF schemaInvalid THEN ResetSchema[] }; NOTIFY transOpened }; AbortTransaction: INTERNAL PROC [] = { IF toolTrans = NIL THEN RETURN; DB.AbortTransaction[toolTrans ! DB.Error, DB.Failure, DB.Aborted => CONTINUE ]; toolTrans _ NIL }; <> RegisterTool: PUBLIC ENTRY PROC [toolName, loadFile: ROPE] ~ { ENABLE UNWIND => NULL; IF InternalRegisterTool[toolName: toolName, loadFile: loadFile] THEN DB.MarkTransaction[toolTrans] }; InternalRegisterTool: INTERNAL PROC[toolName: ROPE, loadFile: ROPE _ "", docFile: ROPE _ "", briefDoc: ROPE _ "", descriptors: LIST OF ROPE _ LIST["NoDesc"] ] RETURNS[success: BOOLEAN] = { ENABLE UNWIND => NULL; DoRegisterTool: PROC[] = { <> alreadyExists: BOOLEAN _ TRUE; toolEntity: DBDefs.Entity _ DB.LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN { toolEntity _ DB.DeclareEntity[toolDomain, toolName]; alreadyExists _ FALSE; }; IF NOT alreadyExists THEN { [] _ DB.CreateRelship[propertyTable[loadFileProp].relation, L2VS[LIST[E2V[toolEntity], S2V[loadFile] ]]]; [] _ DB.CreateRelship[propertyTable[docFileProp].relation, L2VS[LIST[E2V[toolEntity], S2V[docFile]]]]; [] _ DB.CreateRelship[propertyTable[briefDocProp].relation, L2VS[LIST[E2V[toolEntity], S2V[briefDoc]]]]; [] _ DB.CreateRelship[propertyTable[viewerFlavorProp].relation, L2VS[LIST[E2V[toolEntity], S2V[""]]]]; [] _ DB.CreateRelship[propertyTable[namePatternProp].relation, L2VS[LIST[E2V[toolEntity], S2V[""]]]]; [] _ DB.CreateRelship[propertyTable[argumentPatternProp].relation, L2VS[LIST[E2V[toolEntity], S2V[""]]]]; } ELSE { IF loadFile#"" THEN DB.SetP[toolEntity,propertyTable[loadFileProp].relation,1, S2V[loadFile] ]; IF docFile#"" THEN DB.SetP[toolEntity,propertyTable[docFileProp].relation,1, S2V[docFile] ]; IF briefDoc#"" THEN DB.SetP[toolEntity,propertyTable[briefDocProp].relation,1, S2V[briefDoc] ]; }; <> IF descriptors = NIL THEN RETURN; TRUSTED BEGIN <> oldrships: RelshipSet = RelshipsWithEntityField[descriptorRelation, descriptorAttribute, toolEntity]; newValues: ValueSequence _ NEW[ValueSequenceObject[2]]; FOR old: DBDefs.Relship _ NextRelship[oldrships], NextRelship[oldrships] UNTIL old = NIL DO DB.DestroyRelship[old] ENDLOOP; DB.ReleaseRelshipSet[oldrships]; <> newValues[descriptorAttribute] _ Value[entity[toolEntity]]; FOR ds: LIST OF ROPE _ descriptors, ds.rest WHILE ds # NIL DO newValues[descriptionAttribute] _ S2V[descriptors.first]; [] _ CreateRelship[descriptorRelation, newValues] ENDLOOP END }; IF readOnly OR ToolDB = NIL OR toolName = NIL THEN 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] ~ { DoGetToolInfo: PROC[] = { toolEntity: DBDefs.Entity = DB.LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; loadFile _ V2S[GetP[toolEntity, propertyTable[loadFileProp].relation, propertyValueAttribute]]; docFile _ V2S[GetP[toolEntity, propertyTable[docFileProp].relation, propertyValueAttribute]]; briefDoc _ V2S[GetP[toolEntity, propertyTable[briefDocProp].relation, propertyValueAttribute]] }; 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: DBDefs.Entity = DB.LookupEntity[toolDomain, toolName]; IF descriptors = NIL THEN RETURN; TRUSTED BEGIN <> <> oldrships: RelshipSet = RelshipsWithEntityField[descriptorRelation, descriptorAttribute, toolEntity]; newValues: ValueSequence _ NEW[ValueSequenceObject[2]]; FOR old: DBDefs.Relship _ NextRelship[oldrships], NextRelship[oldrships] UNTIL old = NIL DO DB.DestroyRelship[old] ENDLOOP; DB.ReleaseRelshipSet[oldrships]; <> newValues[descriptorAttribute] _ Value[entity[toolEntity]]; FOR ds: LIST OF ROPE _ descriptors, ds.rest WHILE ds # NIL DO newValues[descriptionAttribute] _ S2V[descriptors.first]; [] _ CreateRelship[descriptorRelation, newValues]; descriptors _ descriptors.rest; 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] = { ENABLE UNWIND => NULL; DoGetToolDescriptors: PROC[] = { toolEntity: DBDefs.Entity = LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; BEGIN rships: RelshipSet = RelshipsWithEntityField[descriptorRelation, descriptorAttribute, toolEntity]; FOR old: DBDefs.Relship _ NextRelship[rships], NextRelship[rships] UNTIL old = NIL DO descriptors _ CONS[V2S[DB.GetF[old, descriptionAttribute]], 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; DescriptorSequence: TYPE = REF DescriptorSequenceObject; DescriptorSequenceObject: TYPE = RECORD[SEQUENCE count: CARDINAL OF DescriptorRecord]; DescriptorRecord: TYPE = RECORD[ name: Rope.ROPE, found: BOOL _ FALSE]; DoFindMatch: PROC[] = { rshipSet: DBDefs.RelshipSet; descriptorSequence: DescriptorSequence; descriptorCount: CARDINAL _ 0; IF descriptors = NIL THEN { -- produce the list of all of the tools in the database toolSet: DBDefs.EntitySet = DB.DomainSubset[d: toolDomain, start: First]; FOR nextTool: DBDefs.Entity _ DB.NextEntity[toolSet], DB.NextEntity[toolSet] UNTIL nextTool = NIL DO tools _ CONS[DB.EntityInfo[nextTool].name, tools] ENDLOOP; DB.ReleaseEntitySet[toolSet]; RETURN }; <> FOR l: LIST OF ROPE _ descriptors, l.rest UNTIL l = NIL DO descriptorCount _ descriptorCount+1 ENDLOOP; descriptorSequence _ NEW[DescriptorSequenceObject[descriptorCount]]; descriptorCount _ 0; FOR l: LIST OF ROPE _ descriptors, l.rest UNTIL l = NIL DO descriptorSequence[descriptorCount].name _ l.first; descriptorCount _ descriptorCount+1 ENDLOOP; rshipSet _ RelationSubset[descriptorRelation, descriptorIndex, L2C[LIST[ValueConstraint[rope[low: descriptorSequence[0].name, high: descriptorSequence[0].name]]]], First]; FOR rs: DBDefs.Relship _ NextRelship[rshipSet], NextRelship[rshipSet] UNTIL rs = NIL DO entity: DBDefs.Entity = DB.V2E[DB.GetF[rs, descriptorAttribute]]; descriptionsForEntity: DBDefs.RelshipSet = RelshipsWithEntityField[descriptorRelation, descriptorAttribute, entity]; allFound: BOOL _ TRUE; FOR next: DBDefs.Relship _ NextRelship[descriptionsForEntity], NextRelship[descriptionsForEntity] UNTIL next = NIL DO FOR i: CARDINAL IN [1..descriptorSequence.count) DO IF Rope.Equal[descriptorSequence[i].name, V2S[GetF[next, descriptionAttribute]]] THEN descriptorSequence[i].found _ TRUE ENDLOOP ENDLOOP; ReleaseRelshipSet[descriptionsForEntity]; FOR i: CARDINAL IN [1..descriptorSequence.count) DO allFound _ allFound AND descriptorSequence[i].found; descriptorSequence[i].found _ FALSE ENDLOOP; IF allFound THEN tools _ CONS[DB.EntityInfo[entity].name, tools] ENDLOOP; ReleaseRelshipSet[rshipSet] }; IF NOT CarefullyApply[DoFindMatch] THEN RETURN[NIL] }; InternalLoadTool: INTERNAL PROC [toolName: ROPE, errorStream: IO.STREAM] ~ { loadFile: ROPE = InternalGetToolProps[toolName].loadFile; IF Rope.Equal[loadFile, ""] OR CheckLoaded[loadFile] THEN RETURN; DoCommand[Rope.Cat["Source ", loadFile, "\n"], errorStream]; loadedList _ CONS[loadFile, loadedList] }; LoadTool: PUBLIC ENTRY PROC [toolName: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] ~ { ENABLE UNWIND => NULL; InternalLoadTool[toolName, errorStream] }; 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] ~ { ENABLE UNWIND => NULL; DoApplyTool: INTERNAL PROC[] ~ { command: ROPE; IF DB.LookupEntity[toolDomain, toolName] = NIL THEN RETURN; InternalLoadTool[toolName, errorStream]; 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] ] } }; [] _ CarefullyApply[DoApplyTool] }; DoCommand: PROC[commandLine: ROPE, errorStream: IO.STREAM _ IO.noWhereStream] = { crockCmdHandle.err _ errorStream; crockCmdHandle.out _ IO.noWhereStream; [] _ CommandTool.DoCommandRope[commandLine: commandLine, parent: crockCmdHandle] }; RegisterViewerFlavor: PUBLIC ENTRY PROC [tool: ROPE, flavor: ViewerClasses.ViewerFlavor] ~ { ENABLE UNWIND => NULL; DoRegisterViewerFlavor: PROC = { toolEntity: DBDefs.Entity = LookupEntity[toolDomain, tool]; atomName: Rope.ROPE = IF flavor = NIL THEN NIL ELSE Atom.GetPName[flavor]; IF toolEntity = NIL THEN RETURN; SetP[toolEntity, propertyTable[viewerFlavorProp].relation, propertyValueAttribute, S2V[atomName]] }; IF CarefullyApply[DoRegisterViewerFlavor] THEN DB.MarkTransaction[toolTrans] }; RegisterNamePattern: PUBLIC ENTRY PROC [toolName: ROPE, pattern: ROPE] ~ { ENABLE UNWIND => NULL; DoRegisterNamePattern: PROC = { toolEntity: DBDefs.Entity = LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; SetP[toolEntity, propertyTable[namePatternProp].relation, propertyValueAttribute, S2V[pattern]] }; IF CarefullyApply[DoRegisterNamePattern] THEN DB.MarkTransaction[toolTrans] }; RegisterArgumentPattern: PUBLIC ENTRY PROC [toolName: ROPE, pattern: ROPE] ~ { ENABLE UNWIND => NULL; DoRegisterArgumentPattern: PROC = { toolEntity: DBDefs.Entity = LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; SetP[toolEntity, propertyTable[argumentPatternProp].relation, propertyValueAttribute, 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: DBDefs.Entity = LookupEntity[toolDomain, toolName]; IF toolEntity = NIL THEN RETURN; flavor _ Atom.MakeAtom[V2S[GetP[toolEntity, propertyTable[viewerFlavorProp].relation, propertyValueAttribute]]]; namePattern _ V2S[GetP[toolEntity, propertyTable[namePatternProp].relation, propertyValueAttribute]]; argumentPattern _ V2S[GetP[toolEntity, propertyTable[argumentPatternProp].relation, propertyValueAttribute]] }; 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 = { GetFlavorRelship: PROC[] RETURNS[rship: DBDefs.Relship] ~ { flavor: ROPE = Atom.GetPName[v.class.flavor]; rSet: DBDefs.RelshipSet ~ DB.RelationSubset[propertyTable[viewerFlavorProp].relation, viewerFlavorIndex, DB.L2C[LIST[DBDefs.ValueConstraint[rope[low: flavor]]]], First]; rship _ DB.NextRelship[rSet]; <> IF (DB.V2S[DB.GetF[rship, propertyValueAttribute]]#flavor) THEN rship _ NIL; DB.ReleaseRelshipSet[rSet]; }; viewerName: ROPE = v.name; toolRelation: DBDefs.Relship = IF v.class.flavor # $Container THEN GetFlavorRelship[] ELSE NIL; <<>> toolEntity: DBDefs.Entity _ IF toolRelation = NIL THEN NIL ELSE V2E[GetF[toolRelation, propertyKeyAttribute]]; IF toolEntity = NIL THEN BEGIN toolSet: DBDefs.EntitySet = DomainSubset[d: toolDomain, start: First]; FOR tool: DBDefs.Entity _ NextEntity[toolSet], NextEntity[toolSet] UNTIL tool = NIL DO namePattern: ROPE = V2S[GetP[tool, propertyTable[namePatternProp].relation, propertyValueAttribute]]; pattern: TextFind.Finder = TextFind.CreateFromRope[pattern: namePattern, addBounds: TRUE]; IF TextFind.SearchRope[pattern, viewerName].found 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].relation, propertyValueAttribute]]; pattern: TextFind.Finder = TextFind.CreateFromRope[argPattern]; found: BOOLEAN; at, atEnd: INT; [found ~ found, at ~ at, atEnd ~ atEnd] _ TextFind.SearchRope[pattern, viewerName]; IF found THEN arguments _ Rope.Substr[viewerName, at, atEnd-at]; toolName _ DB.EntityInfo[toolEntity].name 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, DB.Error => TRUSTED{ ERROR LoadError }; DO toolName: ROPE = stream.GetRopeLiteral[ ! IO.EndOfStream => GOTO Done]; toolEntity: DBDefs.Entity = DeclareEntity[toolDomain, toolName, TRUE]; 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.CreateRelship[propertyTable[j].relation, L2VS[LIST[E2V[toolEntity], 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; [] _ CreateRelship[descriptorRelation, L2VS[LIST[E2V[toolEntity], S2V[token]]]] ENDLOOP; DB.MarkTransaction[toolTrans] ENDLOOP; EXITS Done => stream.Close[]; }; InternalWrite: INTERNAL PROC[] = { tools: DBDefs.EntitySet = DB.DomainSubset[d: toolDomain, start: First]; FOR tool: DBDefs.Entity _ NextEntity[tools], NextEntity[tools] UNTIL tool = NIL DO OPEN IO; stream.PutF["\n %g", rope[Convert.RopeFromRope[DB.EntityInfo[tool].name]]]; 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].relation, propertyValueAttribute]]]]] ENDLOOP; BEGIN descRships: DBDefs.RelshipSet = DB.RelshipsWithEntityField[descriptorRelation, descriptorAttribute, tool]; stream.Put[rope[" ( "]]; FOR descr: DBDefs.Relship _ NextRelship[descRships], NextRelship[descRships] UNTIL descr = NIL DO descriptor: ROPE = V2S[GetF[descr, descriptionAttribute]]; stream.PutF[" %g", rope[Convert.RopeFromRope[descriptor]]] ENDLOOP; stream.Put[rope[" ) "]]; DB.ReleaseRelshipSet[descRships]; DB.MarkTransaction[toolTrans] END ENDLOOP; stream.Close[] }; WriteCatalogue: PUBLIC ENTRY PROC [file: Rope.ROPE] = { ENABLE UNWIND => NULL; stream _ FS.StreamOpen[file, $append ! FS.Error => {stream _ NIL; CONTINUE}]; IF stream # NIL THEN [] _ CarefullyApply[InternalWrite] }; <> OpenUp: Booting.RollbackProc = { DB.Initialize[nCachePages: 256] }; CloseTrans: ENTRY Booting.CheckpointProc = { ENABLE UNWIND => NULL; CloseTransaction[] }; NewUserReset: ENTRY UserCredentials.CredentialsChangeProc = { ENABLE UNWIND => NULL; CloseTransaction[]; stopped _ TRUE }; ResetSchema: INTERNAL PROC[] = { toolDomain _ DeclareDomain["Tool", $Tool]; FOR i: [0..6) IN [0..6) DO propertyTable[i].relation _ DeclareProperty[name: propertyTable[i].name, segment: $Tool, fields: L2FS[LIST[propertyKeyField, propertyValueField]]]; IF i = viewerFlavorProp THEN viewerFlavorIndex _ DeclareIndex[propertyTable[i].relation, L2F[LIST[propertyValueAttribute]]] ENDLOOP; descriptorRelation _ DeclareRelation[name: "Descriptor", segment: $Tool, fields: L2FS[LIST[descriptorField, descriptionField]]]; descriptorIndex _ DeclareIndex[descriptorRelation, L2F[LIST[descriptionAttribute]]]; }; ReadCatalogue: PUBLIC ENTRY PROC[file: Rope.ROPE, eraseFirst: BOOL _ FALSE] = TRUSTED { ENABLE BEGIN UNWIND => NULL; LoadError => GOTO Failure; END; DoRead: INTERNAL PROC[] = TRUSTED { IF eraseFirst THEN { toolTrans _ DB.EraseSegment[$Tool, toolTrans]; DB.MarkTransaction[toolTrans]; ResetSchema[] }; Parse[]; DB.MarkTransaction[toolTrans] }; stream _ FS.StreamOpen[file ! FS.Error => {stream _ NIL; CONTINUE}]; IF stream = NIL THEN RETURN; stream _ IOClasses.CreateCommentFilterStream[stream]; IF NOT CarefullyApply[DoRead] THEN { DB.AbortTransaction[toolTrans ! DB.Error => CONTINUE]; toolTrans _ NIL } EXITS Failure => { DB.AbortTransaction[toolTrans ! DB.Error => CONTINUE]; toolTrans _ NIL} }; CarefullyApply: INTERNAL PROC [proc: PROC[]] RETURNS [succeeded: BOOL] ~ { ENABLE DB.Error, DB.Failure, DB.Aborted => {succeeded _ FALSE; CONTINUE}; aborted: BOOL _ FALSE; succeeded _ TRUE; IF stopped THEN RETURN; activity _ TRUE; BEGIN ENABLE DB.Aborted => { aborted _ TRUE; CONTINUE }; OpenTransaction[]; proc[] END; IF NOT aborted THEN RETURN; -- no aborted occurred AbortTransaction[]; OpenTransaction[]; proc[]; -- don't bother trying to restart here -- }; OpenDB: INTERNAL PROC[] ~ { ENABLE DB.Aborted, DB.Failure, DB.Error => CONTINUE; segmentNumber: NAT = 320B; readOnly _ FALSE; DB.Initialize[nCachePages: 256]; DB.DeclareSegment[ToolDB, $Tool, segmentNumber]; <> OpenTransaction[! DB.Error => IF code = ProtectionViolation THEN {readOnly _ TRUE; CONTINUE} ELSE REJECT ]; IF readOnly THEN { <> CloseTransaction[]; DB.DeclareSegment[filePath: ToolDB, segment: $Tool, number: segmentNumber, readonly: TRUE]; OpenTransaction[] } }; OpenIt: Commander.CommandProc = { h: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE; [] _ h.SkipWhitespace[]; IF h.EndOf THEN name _ NIL ELSE name _ h.GetLineRope[]; IF Rope.Equal[name, ""] THEN IF ToolDB # NIL THEN name _ ToolDB ELSE msg _ "Must supply a database name"; EstablishToolDB[name] }; CloseIt: Commander.CommandProc = { Close[] }; <> TRUSTED { Booting.RegisterProcs[c: CloseTrans, r: OpenUp]; Commander.Register[key: "OpenToolDB", proc: OpenIt, doc: "\nOpenToolDB opens a new tool database (closing a previously open one, if necessary)"]; Commander.Register[key: "CloseToolDB", proc: CloseIt, doc: "\nCloseToolDB closes the tool database"]; Process.Detach[ FORK WatchDBActivity[] ] }; END. <<>>