NameDBImpl.Mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Last modified by Swinehart, April 7, 1987 9:54:26 am PDT
DIRECTORY
Atom USING [ MakeAtom ],
BasicTime USING [ FromNSTime, GMT, Now, nullGMT, Period, ToNSTime, Update ],
Commander USING [ CommandProc, Handle, Register ],
CommandTool USING [ NextArgument ],
Convert USING [ CardFromRope ],
DESFace USING [ CorrectParity, EncryptBlock ],
IO,
GVNames USING [ AuthenticateKey, GetConnect, Outcome ],
LoganBerry USING [ Error, Open, OpenDB, ReadEntry, WriteEntry ],
NameDB USING [ AttributeSeq, Authenticity ],
Process USING [ Detach ],
RefID USING [ nullID ],
RefTab USING [ Create, Fetch, Ref, Store ],
Rope USING [ Equal, Find, Length, ROPE, SkipOver, Substr ],
RPC USING [ Conversation, ConversationLevel, EncryptionKey, StartConversation, unencrypted ],
UserProfile USING [ Token ],
VoiceUtils USING [ CurrentRName, CurrentPasskey, NetAddress, NetAddressFromRope, nullNetAddress, RnameToRspec ]
;
NameDBImpl: CEDAR MONITOR
Lock as little as possible
IMPORTS Atom, BasicTime, Commander, CommandTool, Convert, DESFace, GVNames, IO, LoganBerry, Process, RefTab, Rope, RPC, UserProfile, VoiceUtils
EXPORTS NameDB = {
Data
ROPE: TYPE= Rope.ROPE;
AttributeSeq: TYPE = NameDB.AttributeSeq; -- LoganBerry.Entry = LIST OF [$attribute, value]
OpenDB: TYPE = LoganBerry.OpenDB;
nullDB: OpenDB = RefID.nullID;
Error: PUBLIC ERROR [ec: ATOM, explanation: Rope.ROPENIL] = CODE;
Mostly just repeats LoganBerry errors.
DBHandle: TYPE = RECORD [ -- In case there ever have to be instances
dbPrefix: Rope.ROPE,
conversation: RPC.Conversation←RPC.unencrypted,
white: OpenDB←nullDB, -- Name/number database
blue: OpenDB←nullDB, -- Name/Lark/Workstation/Preferences database
red: OpenDB←nullDB -- GV cache database
];
dbHandle: DBHandle; -- There is but one instance now.
minQueryInterval: INT ← 60;
Don't query Grapevine about any given value any more frequently.
conversationLevel: RPC.ConversationLevel ← ECB;
Procedures exported to NameDB
GetAttributes: PUBLIC PROC[rName: ROPE, key: ATOM←$rname, dbType: ATOM←$white]
RETURNS [value: AttributeSeq←NIL] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
openDB: OpenDB = DBFromType[dbType];
IF openDB#nullDB THEN
value ← LoganBerry.ReadEntry[conv: dbHandle.conversation,
db: DBFromType[dbType].openDB, key: key, value: rName].entry;
};
GetAttribute: PUBLIC PROC[
rName: ROPE, attribute: ATOM, default: ROPENIL, key: ATOM←$rname]
RETURNS [value: ROPE←NIL] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
dbKType: ATOM;
dbType: ATOM;
openDB: OpenDB;
attributes: AttributeSeq;
If key#rname, acquire the rname
[dbKType, , openDB] ← WhichDB[key];
IF dbKType#$noProbe THEN {
attributes ← LoganBerry.ReadEntry[conv: dbHandle.conversation,
db: openDB, key: key, value: rName].entry;
rName ← FetchAttribute[attributes, $rname, NIL].value;
IF rName=NIL THEN RETURN;
};
If attribute is not in the same entry, use rName to get the entry it's in.
[dbType, , openDB] ← WhichDB[attribute];
SELECT dbType FROM
= $noProbe => RETURN[rName];
# dbKType =>
attributes ← LoganBerry.ReadEntry[conv: dbHandle.conversation,
db: openDB, key: $rname, value: rName].entry;
ENDCASE;
Obtain the specified attribute
value ← FetchAttribute[attributes, attribute, default].value;
If we're dealing with a timed value, select one of timed.attribute and untimed.attribute
IF value.Equal["#"] THEN {
value ← FetchAttribute[attributes, TAttr[attribute, "time"]].value;
value ← FetchAttribute[attributes, TAttr[attribute,
(IF value#NIL AND RelTime[value] > 0 THEN "timed" ELSE "untimed")], default].value;
};
If we're looking in the Grapevine directory (red pages), do the update.
IF dbType#$red THEN RETURN;
IF attributes=NIL THEN value ← DoGVUpdate[rName, attributes]
ELSE ConsiderGVUpdate[rName, attributes];
};
SetAttribute: PUBLIC PROC[rName: ROPE, attribute: ATOM, value: ROPE] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
dbType: ATOM;
openDB: OpenDB;
attributes: AttributeSeq;
wasTimed: BOOLFALSE;
IF value#NIL THEN SELECT attribute FROM
$larkhost, $workstationhost => {
These secondary keys must be unique. Cancel any previous values.
oldRname: ROPE=GetAttribute[value, $rname, NIL, attribute];
IF oldRname#NIL THEN
SetAttribute[rName: oldRname, attribute: attribute, value: NIL];
};
ENDCASE;
[dbType, , openDB] ← WhichDB[attribute];
IF dbType=$noProbe THEN RETURN;
Can't alter primary key! And deletion of entire entries is done by hand.
IF attributes=NIL THEN attributes ← LIST[[$rname, rName]];
attributes ← GetAttributes[rName, $rname, dbType];
wasTimed ← Rope.Equal[FetchAttribute[attributes, attribute].value, "#"];
attributes ← StoreAttribute[attributes, attribute, value];
IF wasTimed THEN { -- Remove the timed values
attributes ← StoreAttribute[attributes, TAttr[attribute, "timed"], NIL];
attributes ← StoreAttribute[attributes, TAttr[attribute, "untimed"], NIL];
attributes ← StoreAttribute[attributes, TAttr[attribute, "time"], NIL];
};
LoganBerry.WriteEntry[conv: dbHandle.conversation,
db: openDB, entry: attributes, replace: TRUE];
};
SetAttributeTimed: PUBLIC PROC[
rName: Rope.ROPE, attribute: ATOM, value: Rope.ROPE,
time: BasicTime.GMT�sicTime.nullGMT, interval: INT𡤀] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
dbType: ATOM;
keyClass: KeyClass;
openDB: OpenDB;
attributes: AttributeSeq;
untimedValue: Rope.ROPE;
wasUntimed: BOOL;
IF time=BasicTime.nullGMT THEN time ← BasicTime.Update[BasicTime.Now[], interval];
[dbType, keyClass, openDB] ← WhichDB[attribute];
IF keyClass#$notKey THEN
Error[$InvalidAttribute, "SetAttributeTimed cannot be applied to an attribute that is also a key"];
IF attributes=NIL THEN attributes ← LIST[[$rname, rName]];
attributes ← GetAttributes[rName, $rname, dbType];
untimedValue ← FetchAttribute[attributes, attribute].value;
wasUntimed ← ~Rope.Equal[untimedValue, "#"];
IF wasUntimed AND untimedValue#NIL THEN
attributes ← StoreAttribute[attributes, TAttr[attribute, "untimed"], untimedValue];
attributes ← StoreAttribute[attributes, TAttr[attribute, "timed"], value];
attributes ← StoreAttribute[attributes, TAttr[attribute, "time"], TimeRope[time]];
IF wasUntimed THEN attributes ← StoreAttribute[attributes, attribute, "#"];
LoganBerry.WriteEntry[conv: dbHandle.conversation,
db: openDB, entry: attributes, replace: TRUE];
};
Authenticate: PUBLIC PROC[rName: ROPE, key: RPC.EncryptionKey]
RETURNS [authenticity: NameDB.Authenticity] = {
attributes: AttributeSeq ← GetAttributes[rName: rName, dbType: $red];
authRope: ROPE;
IF attributes = NIL THEN attributes ← LIST[[$rname, rName]];
authRope ← FetchAttribute[attributes, $authenticity].value;
authenticity ← IF authRope=NIL THEN NIL ELSE Atom.MakeAtom[authRope];
IF it exists, parse it up and produce a comparison with the proferred key?
SELECT authenticity FROM
NIL, $unknown, $perhaps, $bogus =>
authenticity ← DoGVAuthenticate[rName, key, attributes];
$nonexistent => NULL;
$authentic => authenticity ← GVCheckKey[key, attributes].authenticity;
ENDCASE => ERROR;
ConsiderGVAuthenticate[rName, key, attributes];
If timeStamp is old, update authentication results in background
};
IsAuthenticated: PUBLIC PROC[rName: ROPE]
RETURNS [authenticity: NameDB.Authenticity ← $unknown] = {
attributes: AttributeSeq ← GetAttributes[rName, $rname, $red];
IF attributes#NIL THEN
authenticity ← Atom.MakeAtom[FetchAttribute[attributes, $authenticity, "perhaps"].value];
ConsiderGVUpdate[rName, attributes];
};
HostFromInstance: PUBLIC PROC[instance: Rope.ROPE]
RETURNS [VoiceUtils.NetAddress←VoiceUtils.nullNetAddress] = {
netAddressAsRope: ROPE = GetAttribute[instance, $connect];
IF netAddressAsRope#NIL THEN
RETURN[VoiceUtils.NetAddressFromRope[netAddressAsRope]];
};
Grapevine update.
ConsiderGVUpdate: PROC[rName: ROPE, attributes: AttributeSeq] = {
timeStamp: ROPE = FetchAttribute[attributes, TAttr[$connect, "timestamp"]].value;
IF timeStamp#NIL AND (-RelTime[timeStamp]) < minQueryInterval THEN RETURN;
TRUSTED { Process.Detach[FORK ForkGVUpdate[rName, attributes]]; };
};
ForkGVUpdate: PROC[rName: ROPE, attributes: AttributeSeq] = {
ENABLE Error => CONTINUE; -- Should report it, I guess.
[] ← DoGVUpdate[rName, attributes];
};
DoGVUpdate: PROC[rName: ROPE, attributes: AttributeSeq]
RETURNS[value: ROPENIL] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
info: GVNames.Outcome←notFound;
[info, value] ← GVNames.GetConnect[rName];
IF info#individual THEN RETURN[NIL]; -- Didn't happen.
IF attributes=NIL THEN attributes ← LIST[[$rname, rName]];
IF value#NIL THEN attributes ← StoreAttribute[attributes, $connect, value];
attributes ← StoreAttribute[attributes, TAttr[$connect, "timestamp"], TimeRope[]];
LoganBerry.WriteEntry[conv: dbHandle.conversation,
db: dbHandle.red, entry: attributes, replace: TRUE];
};
ConsiderGVAuthenticate: PROC[
rName: ROPE, key: RPC.EncryptionKey, attributes: AttributeSeq] = {
timeStamp: ROPE = FetchAttribute[attributes, TAttr[$key, "timestamp"]].value;
IF timeStamp#NIL AND (-RelTime[timeStamp]) < minQueryInterval THEN RETURN;
TRUSTED { Process.Detach[FORK ForkGVAuthenticate[rName, key, attributes]]; };
};
ForkGVAuthenticate: PROC[rName:ROPE, key:RPC.EncryptionKey, attributes:AttributeSeq] = {
ENABLE Error => CONTINUE; -- Should report it, I guess.
[] ← DoGVAuthenticate[rName, key, attributes];
};
DoGVAuthenticate: PROC[rName: ROPE, key: RPC.EncryptionKey, attributes: AttributeSeq]
RETURNS[authenticity: NameDB.Authenticity←$perhaps] = {
ENABLE LoganBerry.Error => Error[ec, explanation];
SELECT GVNames.AuthenticateKey[rName, key] FROM
group, notFound => authenticity ← $nonexistent;
individual => authenticity ← $authentic;
protocolError => Error[$GVNamesProtocolViolation, "GV Names protocol violation"];
wrongServer, allDown => authenticity ← $unknown;
badPwd => authenticity ← $bogus;
ENDCASE => Error[$GVNamesProtocolViolation, "Unknown return code"];
IF attributes=NIL THEN attributes ← LIST[[$rname, rName]];
attributes ← StoreAttribute[attributes, $authenticity, IO.PutR[[atom[authenticity]]]];
attributes ← StoreAttribute[attributes, $key, GVCheckKey[key, attributes].keyRope];
attributes ← StoreAttribute[attributes, TAttr[$key, "timestamp"], TimeRope[]];
LoganBerry.WriteEntry[conv: dbHandle.conversation,
db: dbHandle.red, entry: attributes, replace: TRUE];
};
GVCheckKey: PROC[key: RPC.EncryptionKey, attributes: AttributeSeq]
RETURNS[keyRope: ROPE, authenticity: NameDB.Authenticity ← $perhaps] = TRUSTED {
encryptedKey: RPC.EncryptionKey;
cpKey: RPC.EncryptionKey ← key;
retrievedEncryptedKey: RPC.EncryptionKey;
retrievedKeyRope: ROPE;
rkrStream: IO.STREAM;
halves: LONG POINTER TO RECORD[firstHalf: CARD, secondHalf: CARD] ←
LOOPHOLE[LONG[@encryptedKey]];
DESFace.CorrectParity[LOOPHOLE[LONG[@cpKey]]];
DESFace.EncryptBlock[key: LOOPHOLE[cpKey], from: @key, to: LOOPHOLE[halves]];
keyRope ← IO.PutFR["%bB %bB", [cardinal[halves.firstHalf]], [cardinal[halves.secondHalf]]];
halves ← LOOPHOLE[LONG[@retrievedEncryptedKey]];
retrievedKeyRope ← FetchAttribute[attributes, $key].value;
IF retrievedKeyRope=NIL THEN RETURN;
rkrStream ← IO.RIS[retrievedKeyRope];
halves.firstHalf ← Convert.CardFromRope[IO.GetCedarTokenRope[rkrStream].token];
halves.secondHalf ← Convert.CardFromRope[IO.GetCedarTokenRope[rkrStream].token];
authenticity ← IF encryptedKey = retrievedEncryptedKey THEN $authentic ELSE $bogus;
};
Utilities
FetchAttribute: PROC[
attributes: AttributeSeq, attribute: ATOM, default: ROPENIL]
RETURNS [value: ROPE, valueLoc: AttributeSeq←NIL] = {
value ← default;
FOR aL: AttributeSeq ← attributes, aL.rest WHILE aL#NIL DO
IF aL.first.type = attribute THEN RETURN[aL.first.value, aL]; ENDLOOP;
};
ExtractAttribute: PROC[
attributes: AttributeSeq, attribute: ATOM]
RETURNS [newAttributes: AttributeSeq←NIL] = {
IF attributes=NIL THEN RETURN;
IF attributes.first.type=attribute THEN RETURN[attributes.rest];
newAttributes ← attributes;
FOR aL: AttributeSeq ← attributes, aL.rest WHILE aL.rest#NIL DO
IF aL.rest.first.type # attribute THEN LOOP;
aL.rest ← aL.rest.rest;
RETURN;
ENDLOOP;
};
StoreAttribute: PROC[attributes: AttributeSeq, attribute: ATOM, value: ROPE]
RETURNS[newAttributes: AttributeSeq] = {
oldLoc: AttributeSeq;
IF value=NIL OR value.Length[]=value.SkipOver[skip: "\'015\'011"] THEN
RETURN[ExtractAttribute[attributes, attribute]];
newAttributes ← attributes;
oldLoc ← FetchAttribute[attributes, attribute].valueLoc;
IF oldLoc#NIL THEN oldLoc.first ← [attribute, value]
ELSE newAttributes ← Append[attributes, LIST[[attribute, value]]];
};
Append: PROC[
a1, a2: AttributeSeq]
RETURNS [newAttributes: AttributeSeq] = {
IF a2=NIL THEN RETURN[a1];
IF a1=NIL THEN RETURN[a2];
newAttributes ← a1;
FOR aL: AttributeSeq ← a1, aL.rest WHILE aL#NIL DO
IF aL.rest#NIL THEN LOOP;
aL.rest ← a2;
RETURN;
ENDLOOP;
ERROR;
};
DBFromType: PROC[dbType: ATOM] RETURNS [openDB: OpenDB ← nullDB] = {
IF dbHandle.white=nullDB THEN Open[];
RETURN[SELECT dbType FROM
$white => dbHandle.white,
$blue => dbHandle.blue,
$red => dbHandle.red,
ENDCASE => nullDB
];
};
WhichDB: PROC[keyType: ATOM]
RETURNS [
dbType: ATOM←$blue, keyClass: KeyClass←$notKey, openDB: OpenDB ← nullDB] = {
mer: MapEntry ← NARROW[dbMap.Fetch[keyType].val, MapEntry];
IF mer#NIL THEN {
dbType ← mer.dbType;
keyClass ← mer.keyClass;
};
openDB ← DBFromType[dbType];
};
RelTime: PROC[ropeTime: ROPE] RETURNS [relativeToNow: INT] = {
RETURN[BasicTime.Period[from: BasicTime.Now[],
to: BasicTime.FromNSTime[Convert.CardFromRope[ropeTime]]]];
};
TimeRope: PROC [time: BasicTime.GMT ← BasicTime.nullGMT] RETURNS[nowRope: ROPE] = {
IF time = BasicTime.nullGMT THEN time ← BasicTime.Now[];
RETURN[IO.PutFR["%bB", [cardinal[BasicTime.ToNSTime[time]]]]];
};
TAttr: PROC[attribute: ATOM, prefix: Rope.ROPE] RETURNS [tAttr: ATOM] = {
RETURN[Atom.MakeAtom[IO.PutFR["%g.%g", [rope[prefix]], [atom[attribute]]]]]; };
Open: PROC = {
ENABLE LoganBerry.Error => Error[ec, explanation];
prefixValue: IO.Value ← [rope[dbHandle.dbPrefix]];
index: INT ← Rope.Find[dbHandle.dbPrefix, "/", 0];
IF index#0 THEN index← -1;
IF dbHandle.conversation#RPC.unencrypted AND index#-1 THEN {
Possibly make an RPC Conversation for use in LoganBerry calls.
instanceValue: ROPE = dbHandle.dbPrefix.Substr[start: 1, len: index-1];
dotIndex: INT = Rope.Find[dbHandle.dbPrefix, ".", 1];
IF dotIndex # -1 AND dotIndex < index THEN
dbHandle.conversation ← RPC.StartConversation[
VoiceUtils.CurrentRName[], VoiceUtils.CurrentPasskey[], instanceValue,
conversationLevel];
};
dbHandle.white ← LoganBerry.Open[
conv: dbHandle.conversation, dbName: IO.PutFR["%gWhitePages.df", prefixValue]];
dbHandle.blue ← LoganBerry.Open[
conv: dbHandle.conversation, dbName: IO.PutFR["%gBluePages.df", prefixValue]];
dbHandle.red ← LoganBerry.Open[
conv: dbHandle.conversation, dbName: IO.PutFR["%gRedPages.df", prefixValue]];
};
Shell commands
CmdDBOpen: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
dbHandle.dbPrefix ← CommandTool.NextArgument[cmd];
Open[];
};
CmdDBDetails: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
DoDetails[cmd, CommandTool.NextArgument[cmd]];
};
DoDetails: PROC[cmd: Commander.Handle, rName: ROPE] = {
attributes: AttributeSeq ← LIST [[$rname, rName]];
moreAttributes: AttributeSeq ← GetAttributes[rName, $rname, $white];
IF moreAttributes#NIL THEN attributes ← Append[attributes, moreAttributes.rest];
moreAttributes ← GetAttributes[rName, $rname, $blue];
IF moreAttributes#NIL THEN attributes ← Append[attributes, moreAttributes.rest];
moreAttributes ← GetAttributes[rName, $rname, $red];
IF moreAttributes#NIL THEN attributes ← Append[attributes, moreAttributes.rest];
FOR attr: AttributeSeq ← attributes, attr.rest WHILE attr#NIL DO
IO.PutF[cmd.out, "%g: %g\n", [atom[attr.first.type]], [rope[attr.first.value]]];
ENDLOOP;
IO.PutChar[cmd.out, '\n];
};
CmdLarkDebug: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
larkNumber: ROPE ← CommandTool.NextArgument[cmd];
instance: ROPE ← CommandTool.NextArgument[cmd];
IF instance=NIL THEN
instance ← UserProfile.Token[key: "LarktestInstance", default: "Einstein.lark"];
DoOperate[cmd, larkNumber, "D", instance];
};
CmdLarkOperate: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
larkNumber: ROPE ← CommandTool.NextArgument[cmd];
instance: ROPE ← CommandTool.NextArgument[cmd];
IF instance=NIL THEN
instance ← UserProfile.Token[key: "ThrushClientServerInstance", default: "Strowger.lark"];
DoOperate[cmd, larkNumber, "O", instance];
};
DoOperate: PROC[cmd: Commander.Handle, larkNumber: ROPE, mode: ROPE, instance: ROPE] = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
rName: ROPE;
IF larkNumber=NIL THEN RETURN;
larkNumber ← IO.PutFR["173#%g#", [rope[larkNumber]]];
rName ← GetAttribute[larkNumber, $rname, NIL, $larkhost];
IF rName=NIL THEN RETURN;
SetAttribute[rName, $mode, mode];
SetAttribute[rName, $instance, instance];
DoDetails[cmd, rName];
};
Mappings
KeyClass: TYPE = { primary, secondary, notKey };
MapEntry: TYPE = REF MER;
MER: TYPE = RECORD [
dbType: ATOM,
keyClass: KeyClass←$notKey
];
dbMap: RefTab.Ref ← RefTab.Create[];
rname is handled specially
[] ← dbMap.Store[$rname, NEW[MER ← [$noProbe, $primary]]];
whitePages; names, public addresses&numbers
[] ← dbMap.Store[$name, NEW[MER ← [$white, $secondary]]];
[] ← dbMap.Store[$fnm, NEW[MER ← [$white, $secondary]]];
[] ← dbMap.Store[$rgnm, NEW[MER ← [$white, $secondary]]];
[] ← dbMap.Store[$fstnm, NEW[MER ← [$white, $secondary]]];
[] ← dbMap.Store[$officenumber, NEW[MER ← [$white]]];
[] ← dbMap.Store[$homenumber, NEW[MER ← [$white]]];
[] ← dbMap.Store[$officeaddress, NEW[MER ← [$white]]];
[] ← dbMap.Store[$homeaddress, NEW[MER ← [$white]]];
bluePages; private voice system mappings
[] ← dbMap.Store[$larkhost, NEW[MER ← [$blue, $secondary]]];
[] ← dbMap.Store[$workstationhost, NEW[MER ← [$blue, $secondary]]];
[] ← dbMap.Store[$dotune, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$instance, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$interface, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$mode, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$multiring, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$program, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$ringmode, NEW[MER ← [$blue]]];
[] ← dbMap.Store[$ringtune, NEW[MER ← [$blue]]];
Blue is also the default for unknown attributes, so that we can extend the private mappings without recompiling this table.
redPages; cached Grapevine mappings
[] ← dbMap.Store[$connect, NEW[MER ← [$red]]];
[] ← dbMap.Store[$key, NEW[MER ← [$red]]];
[] ← dbMap.Store[$authenticity, NEW[MER ← [$red]]]; -- useful only for IsAuthenticated
Initialization
dbHandle.dbPrefix ← UserProfile.Token["ThrushClientServerInstance", "Strowger.Lark"];
dbHandle.dbPrefix ← IO.PutFR["/%g//%g/",
[rope[dbHandle.dbPrefix]], [rope[VoiceUtils.RnameToRspec[dbHandle.dbPrefix].simpleName]]];
Default prefix value; its default value is "/Strowger.lark//Strowger/"
Commander.Register["NameDBOpen", CmdDBOpen, "Specify location of system databases.
Usage: NameDBOpen <directoryPrefix>
Example: NameDBOpen /Strowger.lark//Strowger/
Example: NameDBOpen ///Users/self.pa/
(first example is the default)\n"];
Commander.Register["NameDBDetails", CmdDBDetails, "Print entire merged DB listing.
Usage: NameDBDetails <rName>\n"];
Commander.Register["LarkDebug", CmdLarkDebug, "Tune Lark into development server, debug mode.
Usage: LarkDebug <larkNumber> [<instance>]
Example: LarkDebug 110 (default instance is UserProfile.LarkTestInstance[])
Example: LarkDebug 110 Curie.lark\n"];
Commander.Register["LarkOperate", CmdLarkOperate, "Tune Lark into operational server, operational mode.
Usage: LarkOperate <larkNumber> [<instance>]
Example: LarkOperate 110 (default instance is UserProfile.ThrushClientServerInstance[])
Example: LarkOperate 110 Curie.lark\n"];
}.