File: DBToolsImpl.mesa
Copyright © 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
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;
Types
ROPE: TYPE = Rope.ROPE;
Global variables for accessing the database
toolDomain: DBDefs.Domain; -- the domain of tools
ToolTypeProc:
PROC[]
RETURNS[DBDefs.TypeCode] = {
RETURN[DB.TypeForDomain[toolDomain]] };
ToolType: DB.TypeSpec = [indirect[ToolTypeProc]];
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
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"]];
a list consisting of all of the properties given above
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
The relationships in this relation have the structure of [tool: Tool, description: ROPE]
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
Establishing the database
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/"];
Set the search rules for the command handle from the list given
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 };
Accessing Tools
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[] = {
see if entity has already been declared
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] ];
now set all of the descriptors for the tool
IF descriptors = NIL THEN RETURN;
TRUSTED
BEGIN
first throw away all of the old ones
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];
now add the new ones
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
first throw away all of the old ones
first throw away all of the old ones
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];
now add the new ones
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 };
now, compute the list of entities matching the first descriptor
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];
check to see that flavor was found
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]
};
Parsing Tool Catalogues
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] };
Opening, closing segment, making/reading catalogue
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];
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
OpenTransaction[!
DB.Error =>
IF code = ProtectionViolation THEN {readOnly ← TRUE; CONTINUE} ELSE REJECT ];
IF readOnly
THEN {
attempt to open for writing failed; open it for reading only
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[] };
Initialization
TRUSTED {
Booting.RegisterProcs[c: CloseTrans, r: OpenUp];
Commander.Register[key: "OpenToolDB", proc: OpenIt, doc: "\nOpenToolDB <file> opens a new tool database (closing a previously open one, if necessary)"];
Commander.Register[key: "CloseToolDB", proc: CloseIt, doc: "\nCloseToolDB <file> closes the tool database"];
Process.Detach[ FORK WatchDBActivity[] ] };
END.