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. πFile: DBToolsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Donahue, April 10, 1986 2:36:15 pm PST (Simplified substantially to handle only those tools that are considered part of the current release) (Changed ticksToWait to 60 -- closes transactions more rapidly now) Last Edited by: Widom, August 22, 1984 11:20:36 am PDT Jack Kent, October 7, 1986 4:20:02 pm PDT Types Global variables for accessing the database Tools have the following properties (each property is a rope). What is given below are the indices into the propertyTable which contains the names and DB.Attributes for each of the properties a list consisting of all of the properties given above The relationships in this relation have the structure of [tool: Tool, description: ROPE] Establishing the database Set the search rules for the command handle from the list given Accessing Tools see if entity has already been declared now set all of the descriptors for the tool first throw away all of the old ones now add the new ones first throw away all of the old ones first throw away all of the old ones now add the new ones now, compute the list of entities matching the first descriptor check to see that flavor was found Parsing Tool Catalogues Opening, closing segment, making/reading catalogue this is a crock to see if the database really can be written to; you need to actually open a transaction to find this out attempt to open for writing failed; open it for reading only Initialization ΚΦ˜šΟi™Icodešœ Οmœ1™<—J™6J™eJ™C™6K™)—J™šΟk ˜ JšœŸœ2˜JšŸœŸœŸœ˜JšœŸœC˜^Jš œŸœŸœŸœŸœ(˜OJšœ?™?Jšœ*Ÿœ‘˜Lš ŸœŸœŸœŸœ"Ÿœ ŸœŸ˜MJ˜6JšŸœ˜—JšŸœŸœŸœ˜(J˜Jšœ Ÿœ‘<˜LJ˜Jšœ ˜ Jšœ Ÿœ˜J˜—š’œŸœ˜šŸœŸœŸ˜ Jšœ˜Jšœ˜JšŸ˜—Jšœ˜J˜—š’œŸœŸœ˜!JšŸœŸœŸœ˜šŸœŸœ Ÿœ˜Jšœ‘*˜>JšŸœ˜—Jšœ Ÿœ˜J˜—J˜š’œŸœŸœŸœ˜JšŸœŸœŸœ˜Jšœ˜Jšœ Ÿœ˜—J˜š’œŸœŸœ˜&JšœŸœŸœ˜JšŸœ ŸœŸœŸœ˜šŸœ˜JšŸœŸœŸœ˜1JšŸœŸœ Ÿœ˜#—JšŸœŸœŸœ˜5Jšœ Ÿœ˜—J˜š’œŸœŸœ˜%JšœŸœ˜šŸœ ŸœŸœ˜JšœŸœ˜7JšŸœŸœ˜&—JšŸœ˜—J˜š’œŸœŸœ˜&JšŸœ ŸœŸœŸœ˜Jš ŸœŸœŸœ Ÿœ Ÿœ˜OJšœ Ÿœ˜——™š ’ œŸœŸœŸœŸœ˜>JšŸœŸœŸœ˜JšŸœ>ŸœŸœ˜eJ˜—š’œŸœŸœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ Ÿœ Ÿœ˜ΌJšŸœŸœŸœ˜š’œŸœ˜J™'JšœŸœŸœ˜JšœŸœ$˜BšŸœ ŸœŸœ˜Jšœ4˜4JšœŸœ˜—J˜J˜šŸœŸœŸœ˜JšœŸœ:Ÿœ%˜jJšœŸœ9Ÿœ"˜fJšœŸœ:Ÿœ#˜hJšœŸœ>Ÿœ˜fJšœŸœ=Ÿœ˜eJšœŸœAŸœ˜i—J˜šŸœ˜šŸœ Ÿ˜JšŸœI˜K—šŸœ Ÿ˜JšŸœG˜I—šŸœ Ÿ˜JšŸœI˜K——˜J˜J˜—Jšœ+™+JšŸœŸœŸœŸœ˜!šŸœŸ˜ Jšœ$™$Jšœe˜eJšœŸœ˜7šŸœFŸœŸœŸ˜[JšŸœ˜JšŸœ˜—JšŸœ˜ Jšœ™Jšœ;˜;š ŸœŸœŸœŸœŸœŸœŸ˜=Jšœ9˜9Jšœ1˜1JšŸ˜—JšŸœ˜——JšŸœ Ÿœ ŸœŸœ ŸœŸœŸœ‘˜LJšœ*˜*Jšœ˜—J˜š’ œŸœŸœŸœ ŸœŸœ Ÿœ˜LJšŸœŸœŸœ˜Jšœ6˜6J˜J˜—š ’œŸœŸœ ŸœŸœŸœ˜dš’ œŸœ˜JšœB˜BJšŸœŸœŸœŸœ˜ Jšœ_˜_Jšœ]˜]Jšœa˜a—JšŸœŸœŸœŸœŸœŸœŸœ˜?J˜J˜—š’œŸœŸœŸœ ŸœŸœŸœ˜^JšŸœŸœŸœ˜JšœI˜IJ˜J˜—š’œŸœŸœŸœ ŸœŸœŸœ˜[JšŸœŸœŸœ˜JšŸœPŸœŸœ˜tJ˜J˜—š’œŸœŸœŸœ ŸœŸœŸœŸœŸœ˜ZJšŸœŸœŸœ˜š’œŸœ˜JšœB˜BJšŸœŸœŸœŸœ˜!šŸœŸ˜ Jšœ$™$Jšœ$™$Jšœe˜eJšœŸœ˜7šŸœFŸœŸœŸ˜[JšŸœ˜JšŸœ˜—JšŸœ˜ Jšœ™Jšœ;˜;š ŸœŸœŸœŸœŸœŸœŸ˜=Jšœ9˜9Jšœ2˜2Jšœ˜JšŸ˜—JšŸœ˜—JšŸœ Ÿœ ŸœŸœ ŸœŸœŸœ‘˜LJšŸœ"ŸœŸœ˜F—Jšœ˜J™—š’œŸœŸœŸœ ŸœŸœŸœŸœŸœ˜\JšŸœŸœŸœ˜š’œŸœ˜ Jšœ?˜?JšŸœŸœŸœŸœ˜ šŸ˜Jšœb˜bšŸœ@ŸœŸœŸ˜UJšœŸœŸœ/˜HJšŸœ˜—JšŸœ˜JšŸœ˜——Jš ŸœŸœ&ŸœŸœŸœ˜