NewWalnutUser.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Willie-Sue Orr, July 18, 1986 1:34:20 pm PDT
special purpose code to construct a Walnut RootFile from the Template-Walnut.Root, store it on the appropriate alpine server and initialize the appropriate files on the server
DIRECTORY
AlpineCmds USING [Copy],
AlpineEnvironment USING [AccessList],
AlpineFS USING [OpenFile, Open, OpenOrCreate, StreamFromOpenFile, WriteProperties],
AlpFile USING [PropertyValuePair],
AlpInstance USING [Create],
AlpTransaction USING [Handle, AssertAlpineWheel, Create, Finish],
DB USING [Segment, TransactionHandle, CloseTransaction, DeclareSegment, EraseSegment, MakeTransHandle],
Commander USING [Handle, Register, CommandProc],
CommandTool USING [ArgumentVector, FileWithSearchRules, NextArgument, Parse],
Convert USING [IntFromRope],
DefaultRemoteNames USING [Get],
FS USING [ComponentPositions, Error, OpenFile, nullOpenFile,
Close, Create, ExpandName, GetInfo, PagesForBytes, SetByteCountAndCreatedTime, SetPageCount, StreamFromOpenFile, StreamOpen],
IO,
RefText USING [line, Fetch, Length, TrustTextAsRope],
Rope,
UserCredentials USING [Get],
WalnutDefs USING [Segment],
WalnutKernelDefs USING [LogInfoFromRoot, RootEntry],
WalnutRegistry USING [CurrentWalnutState],
WalnutSchema USING [SetSchemaVersion],
WalnutStream USING [logFileInfo, WriteEntry];
NewWalnutUser: CEDAR PROGRAM
IMPORTS
AlpineCmds, AlpineFS, AlpInstance, AlpTransaction,
Commander, CommandTool, Convert, DB, DefaultRemoteNames,
FS, IO, RefText, Rope, UserCredentials,
WalnutRegistry, WalnutSchema, WalnutStream
= BEGIN
Types and variables
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
CmdHandle: TYPE = Commander.Handle;
ArgumentVector: TYPE = CommandTool.ArgumentVector;
defaultServer: ROPE = "Luther.alpine";
serverRope: ROPE = "\001ServerAndDirectory\002";
serverLen: NAT = Rope.Length[serverRope];
RootEntry: TYPE = WalnutKernelDefs.RootEntry;
LogInfoFromRoot: TYPE = WalnutKernelDefs.LogInfoFromRoot;
field1: REF TEXT = NEW[TEXT[RefText.line]];
Procedures
InitWalnutFiles: Commander.CommandProc = {
argV: ArgumentVector = CommandTool.Parse[cmd];
userKey, alpineServer: ROPE;
userRName: ROPE = UserCredentials.Get[].name;
IF IsActive[cmd, "InitWalnutFiles"] THEN RETURN;
userKey ← CommandTool.NextArgument[cmd];
IF userKey = NIL THEN userKey ← IO.PutFR["%g's Mail Database", IO.rope[userRName]];
alpineServer ← CommandTool.NextArgument[cmd];
DoMailInit[cmd, userRName, userKey, alpineServer, FALSE];
};
InitForOtherUser: Commander.CommandProc = {
argV: ArgumentVector = CommandTool.Parse[cmd];
userKey, alpineServer, userRName: ROPE;
IF IsActive[cmd, "NewWalnutUser"] THEN RETURN;
userRName ← CommandTool.NextArgument[cmd];
IF userRName = NIL THEN {
cmd.out.PutRope["\n You must supply a userRName - quitting\n"];
RETURN
};
IF userRName.Find["."] = -1 THEN   -- need a registry
userRName ← userRName.Cat[".", DefaultRemoteNames.Get[].registry];
userKey ← IO.PutFR["%g's Mail Database", IO.rope[userRName]];
alpineServer ← CommandTool.NextArgument[cmd];
DoMailInit[cmd, userRName, userKey, alpineServer, TRUE];
};
JustDoFiles: Commander.CommandProc = {
rootFile: ROPE;
argV: ArgumentVector = CommandTool.Parse[cmd];
IF IsActive[cmd, "JustDoFiles"] THEN RETURN;
rootFile ← CommandTool.NextArgument[cmd];
IF rootFile = NIL THEN RETURN;
[] ← InitFiles[rootFile, cmd, FALSE];
};
IsActive: PROC[cmd: Commander.Handle, which: ROPE] RETURNS[is: BOOL] = {
IF WalnutRegistry.CurrentWalnutState[] # active THEN RETURN[FALSE];
cmd.err.PutF["\n*** You must destroy the Walnut Control window before doing %g - quitting\n", IO.rope[which]];
RETURN[TRUE];
};
DoMailInit: PROC[
cmd: CmdHandle, userRName, userKey, alpineServer: ROPE, masquerade: BOOL] = {
Creates a rootFile, stores it on alpine, and initializes all the files names therein, erases the database and sets its version stamp to the correct value. Returns FALSE if operation fails.
localName, remoteName: ROPE;
serverAndDirectory: ROPE;
outcome: ROPE;
IF alpineServer = NIL THEN alpineServer ← defaultServer;
IF alpineServer.Find["."] = -1 THEN alpineServer ← alpineServer.Concat[".alpine"];
serverAndDirectory ←
IO.PutFR["[%g]<%g>", IO.rope[alpineServer], IO.rope[userRName] ];
localName ← IO.PutFR["///Users/%g/Walnut.Root", IO.rope[userRName] ];
IF ~CreateRootFile[cmd, masquerade, userKey, localName, serverAndDirectory, userRName]
THEN RETURN;
remoteName ← Rope.Concat[serverAndDirectory, "Walnut.Root"];
outcome ← CopyLocalToAlpineFile[
  alpineFile: remoteName, from: localName, masquerade: masquerade];
cmd.out.PutRope[outcome];
[] ← InitFiles[remoteName, cmd, masquerade];
};
DoOtherInit: Commander.CommandProc = {
Creates a rootFile, stores it on alpine, and initializes all the files names therein, erases the database and sets its version stamp to the correct value. Returns FALSE if operation fails.
This is for non-mail walnut databases
argV: ArgumentVector = CommandTool.Parse[cmd];
userKey, localName, remoteName, baseName: ROPE;
serverAndDirectory: ROPE;
userRName: ROPE = UserCredentials.Get[].name;
IF cmd = NIL THEN RETURN;
IF IsActive[cmd, "InitOtherWalnutFiles"] THEN RETURN;
baseName ← CommandTool.NextArgument[cmd];
IF baseName = NIL THEN {
cmd.out.PutRope["No base name for database given - quitting\n"];
RETURN;
};
IF baseName.Fetch[0] = '[ THEN serverAndDirectory ← baseName
ELSE IF baseName.Fetch[0] = '/ THEN
serverAndDirectory ← FS.ExpandName[baseName].fullFName
ELSE serverAndDirectory ←
IO.PutFR["[%g]<%g>", IO.rope[defaultServer], IO.rope[baseName]];
userKey ← CommandTool.NextArgument[cmd];
IF userKey = NIL THEN
userKey ← IO.PutFR["%g's Walnut Database", IO.rope[baseName]];
localName ← IO.PutFR["///Users/%g/Walnut.Root", IO.rope[userRName] ];
IF ~CreateRootFile[cmd, FALSE, userKey, localName, serverAndDirectory, baseName]
THEN RETURN;
remoteName ← Rope.Concat[serverAndDirectory, "Walnut.Root"];
AlpineCmds.Copy[to: remoteName, from: localName];
[] ← InitFiles[remoteName, cmd, FALSE];
};
Confirm: PROC[cmd: CmdHandle] RETURNS[continue: BOOL] = {
char: CHAR;
line: ROPE;
cmd.err.PutRope["\nType y(CR) or Y(CR) or CR to continue anyway. "];
line ← cmd.in.GetLineRope[];
IF line.Length[] = 0 THEN RETURN[TRUE];  -- only CR typed
continue ← (char ← line.Fetch[0]) = 'y OR char = 'Y OR char = '\n;
};
CreateRootFile: PROC[
cmd: CmdHandle, masquerade: BOOL,
userKey, localName, serverAndDirectory, mailForName: ROPE] RETURNS[BOOL] = {
template, rootStream: STREAM;
rootOF: FS.OpenFile;
templateRoot: ROPE;
BEGIN ENABLE UNWIND => {
IF template # NIL THEN template.Close[];
IF rootStream # NIL THEN rootStream.Close[];
IF rootOF # FS.nullOpenFile THEN FS.Close[rootOF];
};
templateRoot ← CommandTool.FileWithSearchRules["Template-Walnut", ".root", cmd, FALSE];
IF templateRoot = NIL THEN
{ cmd.out.PutRope["Couldn't find Template-Walnut.root"]; RETURN[FALSE] };
template ← FS.StreamOpen[templateRoot ! FS.Error =>
{ cmd.out.PutRope[error.explanation]; template ← NIL; CONTINUE}];
IF template = NIL THEN RETURN[FALSE];
rootOF ← FS.Create[name: localName, setKeep: TRUE, keep: 2 ! FS.Error => {
cmd.out.PutRope[error.explanation];
rootOF ← FS.nullOpenFile;
template.Close[];
CONTINUE}];
IF rootOF = FS.nullOpenFile THEN RETURN[FALSE];
rootStream ← FS.StreamFromOpenFile[openFile: rootOF, accessRights: $write ! FS.Error => {
cmd.out.PutRope[error.explanation];
template.Close[];
FS.Close[rootOF];
rootStream ← NIL;
CONTINUE}];
IF rootStream = NIL THEN RETURN[FALSE];
DO
BEGIN
line: REF TEXT;
pos: INTEGER;
IF template.EndOf[] THEN EXIT;
line ← template.GetLine[field1 ! IO.EndOfStream => GOTO done];
IF Rope.Find[RefText.TrustTextAsRope[line], "UserSuppliedKey"] = 0 THEN {
rootStream.PutRope[userKey];
rootStream.PutChar['\n];
LOOP
};
IF Rope.Find[RefText.TrustTextAsRope[line], "UserRName"] = 0 THEN {
rootStream.PutRope[mailForName];
rootStream.PutChar['\n];
LOOP
};
IF (pos← Rope.Find[RefText.TrustTextAsRope[line], serverRope]) = -1 THEN {
rootStream.PutText[line];
rootStream.PutChar['\n];
LOOP
};
FOR i: NAT IN [0 .. pos) DO
rootStream.PutChar[RefText.Fetch[line, i]];
ENDLOOP;
rootStream.PutRope[serverAndDirectory];
FOR i: NAT IN [pos+serverLen .. RefText.Length[line]) DO
rootStream.PutChar[RefText.Fetch[line, i]];
ENDLOOP;
rootStream.PutChar['\n];
EXITS
done => EXIT;
END;
ENDLOOP;
template.Close[];
rootStream.Flush[];
rootStream.Close[];
RETURN[TRUE];
END;
};
InitFiles: PROC[rootFile: ROPE, cmd: CmdHandle, masquerade: BOOL] RETURNS[BOOL]= {
rootEntry: RootEntry;
logInfoList: LIST OF LogInfoFromRoot;
logInfoCount: INT ← 0;
keyValue, mailFor, dbName, newMailLog, readArchiveLog: ROPE;
fileFailure: ROPE = "Couldn't initialize file %g\n";
rootStream: STREAM;
finished: BOOL FALSE;
fileStore: ROPE = rootFile.Substr[start: 1, len: rootFile.SkipTo[1, "]"] - 1];
openFile: AlpineFS.OpenFile;
transHandle: AlpTransaction.Handle;
IF rootFile = NIL THEN
{ cmd.out.PutRope["\nNo file specified\n"]; RETURN[FALSE] };
transHandle ← GetTransHandle[fileStore];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
openFile ← AlpineFS.Open[name: rootFile, access: $read, transHandle: transHandle !
FS.Error => CONTINUE];
IF openFile = FS.nullOpenFile THEN {
cmd.out.PutF["Can't open rootFile: %g\n", IO.rope[rootFile] ];  -- couldn't open
RETURN[FALSE]
};
rootStream ← AlpineFS.StreamFromOpenFile[openFile];
BEGIN ENABLE IO.EndOfStream, IO.Error => GOTO cantParse;
rootStream.SetIndex[0];
DO
curPos: INT ← rootStream.GetIndex[];
rootEntry← NextRootEntry[rootStream];
DO
IF rootEntry.ident.Equal["Key", FALSE] THEN {
keyValue← rootEntry.value; EXIT
};
IF rootEntry.ident.Equal["MailFor", FALSE] THEN {
mailFor← rootEntry.value; EXIT
};
IF rootEntry.ident.Equal["Database", FALSE] THEN {
dbName← rootEntry.value; EXIT
};
IF rootEntry.ident.Equal["NewMailLog", FALSE] THEN {
newMailLog← rootEntry.value; EXIT
};
IF rootEntry.ident.Equal["ReadArchiveLog", FALSE] THEN {
readArchiveLog← rootEntry.value; EXIT
};
IF rootEntry.ident.Equal["LogInfo", FALSE] THEN {
logInfoList ← CONS[rootEntry.info, logInfoList];
logInfoCount ← logInfoCount + 1;
EXIT
};
IF rootEntry.ident.Equal["End", FALSE] THEN {
finished← TRUE;
EXIT
};
rootStream.Close[ ! IO.Error, FS.Error => CONTINUE];
cmd.out.PutF["Unknown entry in RootFile at pos %g\n", IO.int[curPos]];
RETURN[FALSE]
ENDLOOP;
IF finished THEN EXIT;
ENDLOOP;
rootStream.Close[ ! IO.Error, FS.Error => CONTINUE];
EXITS
cantParse => {
rootStream.Close[ ! IO.Error, FS.Error => CONTINUE];
cmd.out.PutRope["Couldn't parse root file\n"];
RETURN[FALSE]
};
END;
IF keyValue.Length[] = 0 OR dbName.Length[] = 0 OR mailFor.Length[] = 0 OR
newMailLog.Length[] = 0 OR readArchiveLog.Length[] = 0 OR logInfoCount # 2 THEN {
cmd.out.PutRope["Incomplete RootFile - missing entries\n"];
RETURN[FALSE]
};
check that all the alpine-server names are the same
BEGIN
cp: FS.ComponentPositions;
fn: ROPE;
oServer: ROPE;
CheckServer: PROC[name: ROPE] RETURNS[ok: BOOL] = {
[fn, cp, ] ← FS.ExpandName[name];
oServer ← Rope.Substr[fn, cp.server.start, cp.server.length];
IF ~(ok ← Rope.Equal[fileStore, oServer, FALSE]) THEN
cmd.out.PutF["\nserver for file %g does not agree with that for %g\n",
IO.rope[name], IO.rope[rootFile]];
};
IF ~CheckServer[dbName] THEN RETURN[FALSE];
IF ~CheckServer[newMailLog] THEN RETURN[FALSE];
IF ~CheckServer[readArchiveLog] THEN RETURN[FALSE];
IF ~CheckServer[logInfoList.first.fileName] THEN RETURN[FALSE];
IF ~CheckServer[logInfoList.rest.first.fileName] THEN RETURN[FALSE];
END;
cmd.out.PutRope["\n Initializing log files\n"];
IF ~EnsureEmptyFile[cmd, newMailLog, fileStore, masquerade, 200] THEN RETURN[FALSE];
IF ~EnsureEmptyFile[cmd, readArchiveLog, fileStore, masquerade, 0] THEN RETURN[FALSE];
FOR lil: LIST OF LogInfoFromRoot ← logInfoList, lil.rest UNTIL lil = NIL DO
seqNo: ROPE ← lil.first.logSeqNo;
fileName: ROPE = lil.first.fileName;
openFile: AlpineFS.OpenFile;
IF seqNo.Fetch[0] # 'E THEN {  -- write the LogFileInfo entry in the file
strm: STREAM;
realSeqNo: INT;
transHandle: AlpTransaction.Handle;
IF ~EnsureEmptyFile[cmd, fileName, fileStore, masquerade, 200] THEN RETURN[FALSE];
transHandle ← GetTransHandle[fileStore];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
openFile ← AlpineFS.Open[name: fileName, access: $write, transHandle: transHandle];
strm ← AlpineFS.StreamFromOpenFile[openFile, $write];
realSeqNo ← Convert.IntFromRope[seqNo];
WalnutStream.logFileInfo.key ← keyValue.ToRefText[];
WalnutStream.logFileInfo.internalFileID ← lil.first.internalFileID;
WalnutStream.logFileInfo.logSeqNo ← realSeqNo;
[]← WalnutStream.WriteEntry[strm, WalnutStream.logFileInfo];
strm.Close[];
}
ELSE
IF ~EnsureEmptyFile[cmd, fileName, fileStore, masquerade, 0] THEN RETURN[FALSE];
ENDLOOP;
BEGIN
inaccessible: BOOLFALSE;
cmd.out.PutRope[" Initializing segment file\n"];
BEGIN
segment: DB.Segment = $Walnut;
tHandle: AlpTransaction.Handle = GetTransHandle[fileStore];
transHandle: DB.TransactionHandle = DB.MakeTransHandle[tHandle];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE];
DB.DeclareSegment[
filePath: dbName,
segment: segment,
readonly: FALSE, nPagesInitial: 256, nPagesPerExtent: 256];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE];
[] ← DB.EraseSegment[segment, transHandle];  -- leaves trans open
IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE];
WalnutSchema.SetSchemaVersion[segment ! ABORTED => GOTO cant];
DB.CloseTransaction[transHandle];
EXITS
cant => {
cmd.out.PutF["Couldn't initialize database %g\n", IO.rope[dbName] ];
RETURN[FALSE]
};
END;
END;
cmd.out.PutF[" Walnut files have been properly intialized\n"];
RETURN[TRUE];
};
NextRootEntry: PROC[strm: STREAM] RETURNS[rte: RootEntry] = {
[]← strm.SkipWhitespace[flushComments: TRUE];
rte.ident ← strm.GetTokenRope[].token;
IF rte.ident.Equal["End", FALSE] THEN RETURN;
[]← strm.SkipWhitespace[flushComments: TRUE];
IF rte.ident.Equal["LogInfo", FALSE] THEN {
lif: LogInfoFromRoot;
lif.fileName ← strm.GetTokenRope[IO.IDProc].token;
[]← strm.SkipWhitespace[flushComments: TRUE];
lif.internalFileID ← strm.GetInt[];
[]← strm.SkipWhitespace[flushComments: TRUE];
lif.seqNoPos ← strm.GetIndex[];
lif.logSeqNo ← strm.GetTokenRope[].token;
[]← strm.SkipWhitespace[flushComments: TRUE];
rte.info ← lif;
}
ELSE
IF rte.ident.Equal["Key", FALSE] THEN rte.value ← strm.GetLineRope[]
ELSE {
rte.value ← strm.GetTokenRope[IO.IDProc].token;
[]← strm.SkipWhitespace[flushComments: TRUE];
};
RETURN[rte];
};
nonZeroLen: ROPE = "File %g has nonZero (%g bytes) length\n";
EnsureEmptyFile: PROC[
cmd: CmdHandle, fileName, fileStore: ROPE, masquerade: BOOL, pagesWanted: INT ← 2]
 RETURNS[ok: BOOL] = {
strm: STREAM;
openFile: AlpineFS.OpenFile;
numPages, length: INT;
transHandle: AlpTransaction.Handle = GetTransHandle[fileStore];
prop: highWaterMark AlpFile.PropertyValuePair = [highWaterMark[0]];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
openFile ← AlpineFS.OpenOrCreate[name: fileName, pages: pagesWanted,
transHandle: transHandle];
strm ← AlpineFS.StreamFromOpenFile[openFile, $write];
length ← strm.GetLength[];
IF length # 0 THEN {
continue: BOOLFALSE;
cmd.out.PutRope[IO.PutFR[nonZeroLen, IO.rope[fileName], IO.int[length]]];
continue ← Confirm[cmd];
IF ~continue THEN { strm.Close[]; RETURN[FALSE] };
};
numPages← FS.GetInfo[openFile].pages;
IF pagesWanted > numPages THEN FS.SetPageCount[openFile, pagesWanted];
strm.SetIndex[0];
strm.SetLength[0];
strm.Flush[];  -- make it notice the SetLength
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
AlpineFS.WriteProperties[openFile, LIST[prop]];
strm.Close[];
RETURN[TRUE];
};
CopyLocalToAlpineFile: PROC[
 alpineFile: Rope.ROPE, from: Rope.ROPE, masquerade: BOOL]
   RETURNS[Rope.ROPE] = {
openFile: AlpineFS.OpenFile;
localStrm, alpStrm: STREAM;
transHandle: AlpTransaction.Handle;
pages, bytes: INT;
fileStore: ROPE = alpineFile.Substr[start: 1, len: alpineFile.SkipTo[1, "]"] - 1];
localStrm ← FS.StreamOpen[from];
pages ← FS.PagesForBytes[bytes ← localStrm.GetLength[]];
transHandle ← GetTransHandle[fileStore];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
openFile ← GetAlpineFile[alpineFile, pages, transHandle];
alpStrm ← AlpineFS.StreamFromOpenFile[openFile, $write];
alpStrm.SetIndex[0];
localStrm.SetIndex[0];
CopyStreams[from: localStrm, to: alpStrm, transHandle: transHandle, masquerade: masquerade];
alpStrm.Flush[];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
openFile.SetByteCountAndCreatedTime[ bytes: bytes ];
IF AlpTransaction.Finish[transHandle, commit] # commit THEN {
FS.Close[openFile ! FS.Error => CONTINUE];
localStrm.Close[];
RETURN[IO.PutFR[" commit failed for %g", IO.rope[alpineFile]]];
};
FS.Close[openFile ! FS.Error => CONTINUE];
localStrm.Close[];
RETURN[IO.PutFR["\n\n****%g created, copied from %g", IO.rope[alpineFile], IO.rope[from]]];
};
GetTransHandle: PROC[fileStore: ROPE] RETURNS[AlpTransaction.Handle] =
{ RETURN[ AlpTransaction.Create[instHandle:AlpInstance.Create[fileStore: fileStore]] ] };
GetAlpineFile: PROC[alpineFile: Rope.ROPE, pages: INT, transHandle: AlpTransaction.Handle]
RETURNS[openFile: AlpineFS.OpenFile] = {
initialAccess: AlpineEnvironment.AccessList = LIST[ "owner" ];
openFile ←
AlpineFS.OpenOrCreate[ name: alpineFile, pages: pages, transHandle: transHandle ];
AlpineFS.WriteProperties[ file: openFile, properties: LIST[ [modifyAccess[initialAccess]], [readAccess[initialAccess]] ] ];
};
copyBuffer: REF TEXTNEW[TEXT[8*512]];
CopyStreams: PROC[
 from, to: STREAM, transHandle: AlpTransaction.Handle, masquerade: BOOL] = {
count: INT ← 0;
DO
IF from.GetBlock[copyBuffer, 0, 8*512] = 0 THEN EXIT;
to.PutBlock[copyBuffer];
IF (count ← count + 1) >= 10 THEN {
to.Flush[];
IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE];
count ← 0;
};
ENDLOOP
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Commander.Register["InitWalnutFiles", InitWalnutFiles,
 "Create the files for a Walnut Mail Databse"];
Commander.Register["NewWalnutUser", InitForOtherUser,
 "Create the files for a Walnut Mail Databse for someone else - AlpineWheels only"];
Commander.Register["InitOtherWalnutFiles", DoOtherInit, "Create the files for walnut"];
Commander.Register["JustDoFiles", JustDoFiles, "Create the files using an existing rootFile"];
END.