CreateWalnutWorld.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
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
Last Edited by: Willie-Sue, January 8, 1986 11:28:21 am PST
DIRECTORY
AlpineCmds USING [Copy],
AlpineFS USING [OpenFile, OpenFileFromStream],
Commander USING [Handle, Register, CommandProc],
CommandTool USING [FileWithSearchRules],
Convert USING [IntFromRope],
File USING [Error, Handle, Info, PageCount, Read, wordsPerPage],
FS
USING [ComponentPositions, Error, OpenFile, nullOpenFile,
Close, Create, ExpandName, GetInfo, Open, SetPageCount, StreamFromOpenFile, StreamOpen],
FSBackdoor USING [GetFileHandle],
IagoOps USING [FileError],
IO,
RefText USING [line, Fetch, Length, TrustTextAsRope],
Rope,
UserCredentials USING [Get],
VM
USING [AddressForPageNumber, Allocate, Free, Interval, PagesForWords, nullInterval,
SwapIn, Unpin],
WalnutDefs USING [Segment],
WalnutKernelDefs USING [LogInfoFromRoot, RootEntry],
WalnutRoot USING [EraseDB, Open, Shutdown, StartTransaction],
WalnutSchema USING [SetSchemaVersion],
WalnutStream USING [logFileInfo, Open, WriteEntry, SetHighWaterMark];
Procedures
CheckAndInit: Commander.CommandProc = {DoInit[cmd, TRUE]};
InitWalnutFiles: Commander.CommandProc = {DoInit[cmd, FALSE]};
JustDoFiles: Commander.CommandProc = {
rootFile: ROPE;
h: IO.STREAM;
IF cmd = NIL THEN RETURN;
h ← IO.RIS[cmd.commandLine];
rootFile ←
h.GetTokenRope[IO.IDProc ! IO.EndOfStream => { rootFile ← NIL; CONTINUE }].token;
h.Close[];
IF rootFile = NIL THEN RETURN;
[] ← InitFiles[rootFile, cmd];
};
DoInit:
PROC[cmd: Commander.Handle, checkDumpFile:
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.
userKey, alpineServer, localName, remoteName: ROPE;
userRName: ROPE = UserCredentials.Get[].name;
serverAndDirectory: ROPE;
h: IO.STREAM;
IF cmd = NIL THEN RETURN;
h ← IO.RIS[cmd.commandLine];
userKey ← h.GetRopeLiteral[ ! IO.EndOfStream => { userKey ← NIL; CONTINUE }];
IF userKey =
NIL
THEN
userKey ← IO.PutFR["%g's Mail Database", IO.rope[userRName]]
ELSE alpineServer ← h.GetTokenRope[
IO.IDProc !
IO.EndOfStream =>
{ alpineServer ← NIL; CONTINUE }].token;
h.Close[];
IF alpineServer = NIL THEN alpineServer ← defaultServer;
serverAndDirectory ←
IO.PutFR["[%g.alpine]<%g>", IO.rope[alpineServer], IO.rope[userRName] ];
localName ← IO.PutFR["///Users/%g/Walnut.Root", IO.rope[userRName] ];
IF checkDumpFile
THEN {
dumpFile: ROPE = IO.PutFR["///Walnut/%g/Walnut.tempLog", IO.rope[userRName] ];
ok: BOOL ← FALSE;
handle: File.Handle;
openFile: FS.OpenFile;
openFile ←
FS.Open[dumpFile !
FS.Error =>
{ cmd.out.PutRope[error.explanation]; CONTINUE} ];
IF openFile # FS.nullOpenFile THEN handle ← FSBackdoor.GetFileHandle[openFile];
IF handle =
NIL
THEN {
cmd.out.PutF["\nDump file %g not found.", IO.rope[dumpFile] ];
ok ← Confirm[cmd];
IF ~ok THEN cmd.out.PutRope["Not continuing\n"];
}
ELSE ok ← CheckPages[handle, dumpFile, cmd.out];
IF openFile # FS.nullOpenFile THEN FS.Close[openFile];
IF ~ok THEN RETURN;
};
IF ~CreateRootFile[cmd, userKey, localName, serverAndDirectory, userRName]
THEN RETURN;
remoteName ← Rope.Concat[serverAndDirectory, "Walnut.Root"];
AlpineCmds.Copy[to: remoteName, from: localName];
[] ← InitFiles[remoteName, cmd];
};
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
userKey, localName, remoteName, baseName: ROPE;
serverAndDirectory: ROPE;
userRName: ROPE = UserCredentials.Get[].name;
h: IO.STREAM;
IF cmd = NIL THEN RETURN;
h ← IO.RIS[cmd.commandLine];
baseName ← h.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
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.alpine]<%g>", IO.rope[defaultServer], IO.rope[baseName]];
userKey ← h.GetRopeLiteral[ ! IO.EndOfStream => CONTINUE];
h.Close[];
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, userKey, localName, serverAndDirectory, baseName]
THEN RETURN;
remoteName ← Rope.Concat[serverAndDirectory, "Walnut.Root"];
AlpineCmds.Copy[to: remoteName, from: localName];
[] ← InitFiles[remoteName, cmd];
};
Confirm:
PROC[cmd: Commander.Handle]
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: Commander.Handle,
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];
END;
ENDLOOP;
template.Close[];
rootStream.Flush[];
rootStream.Close[];
RETURN[TRUE];
END;
};
InitFiles:
PROC[rootFile:
ROPE, cmd: Commander.Handle]
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;
IF rootFile =
NIL
THEN
{ cmd.out.PutRope["\nNo file specified\n"]; RETURN[FALSE] };
rootStream ← WalnutStream.Open[name: rootFile, readOnly: TRUE].strm;
IF rootStream =
NIL
THEN
{
cmd.out.PutF["Can't open rootFile: %g\n", IO.rope[rootFile] ]; -- couldn't open
RETURN[FALSE]
};
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;
rServer, 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[rServer, oServer,
FALSE])
THEN
cmd.out.PutF["\nserver for file %g does not agree with that for %g\n",
IO.rope[name], IO.rope[rootFile]];
};
[fn, cp, ] ← FS.ExpandName[rootFile];
rServer ← Rope.Substr[fn, cp.server.start, cp.server.length];
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, 200] THEN RETURN[FALSE];
IF ~EnsureEmptyFile[cmd, readArchiveLog, 0]
THEN
RETURN[
FALSE];
FOR lil:
LIST
OF LogInfoFromRoot ← logInfoList, lil.rest
UNTIL lil =
NIL
DO
seqNo: ROPE ← lil.first.logSeqNo;
fileName: ROPE;
IF seqNo.Fetch[0] # 'E
THEN {
-- write the LogFileInfo entry in the file
strm: STREAM;
realSeqNo: INT;
IF ~EnsureEmptyFile[cmd, fileName← lil.first.fileName, 200] THEN RETURN[FALSE];
strm ← WalnutStream.Open[fileName].strm;
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← lil.first.fileName, 0] THEN RETURN[FALSE];
ENDLOOP;
BEGIN
inaccessible: BOOL ← FALSE;
cmd.out.PutRope[" Initializing segment file\n"];
BEGIN
segment: WalnutDefs.Segment;
segment ← WalnutRoot.Open[
rootName: rootFile, readOnly: FALSE, newSegmentOk: TRUE].segment;
WalnutRoot.StartTransaction[];
WalnutRoot.EraseDB[];
WalnutSchema.SetSchemaVersion[segment ! ABORTED => GOTO cant];
WalnutRoot.Shutdown[];
EXITS
cant =>
{
WalnutRoot.Shutdown[];
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: Commander.Handle, fileName:
ROPE, pagesWanted:
INT ← 2]
RETURNS[ok: BOOL] = {
strm: STREAM ← WalnutStream.Open[name: fileName].strm;
page paramter to AlpineFS.Open is ignored
numPages: INT;
openFile: AlpineFS.OpenFile ← AlpineFS.OpenFileFromStream[strm];
length: INT ← strm.GetLength[];
IF length # 0
THEN
{
continue: BOOL ← FALSE;
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];
[] ← WalnutStream.SetHighWaterMark[strm, 0, pagesWanted];
strm.Close[];
RETURN[TRUE];
};
CheckPages:
PROC[handle: File.Handle, name:
ROPE, out:
STREAM]
RETURNS[ok:
BOOL] = {
ENABLE
FS.Error => {
out.PutRope[" ... "];
IF error.code = $invalidPropertyPage
THEN out.PutRope["not an FS file"]
ELSE out.PutRope[error.explanation];
CONTINUE
};
size: File.PageCount ← File.Info[handle].size;
pageSpace: VM.Interval ← VM.nullInterval;
numPages: INT = 25;
ok ← TRUE;
{
ENABLE UNWIND => FreeSpace[pageSpace];
data: LONG POINTER;
[pageSpace, data] ← GetSpace[numPages];
out.PutF["\nReading pages from %g file (%g pages)\n", IO.rope[name], IO.int[size]];
TRUSTED {
i: File.PageCount ← 0;
WHILE i < size
DO
IF i # 0
THEN
IF i
MOD 100 = 0
THEN
IF i MOD 1000 = 0 THEN out.PutF["(%g)", IO.int[i]] ELSE out.PutChar['~];
File.Read[handle, [i], numPages, data ! File.Error => {
IF why # unknownPage
THEN {
out.PutF["\nFile error reading page %g\n", IO.int[diskPage]];
out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]];
ok ← FALSE;
};
EXIT}];
i ← i + numPages;
ENDLOOP;
};
FreeSpace[pageSpace];
};
};
GetSpace:
PROC[num:
INT]
RETURNS[space:
VM.Interval, data:
LONG
POINTER] =
TRUSTED {
space ← VM.Allocate[VM.PagesForWords[num*File.wordsPerPage]];
data ← VM.AddressForPageNumber[space.page];
VM.SwapIn[interval: space, kill: TRUE, pin: TRUE];
};
FreeSpace:
PROC[space:
VM.Interval] =
TRUSTED {
IF space = VM.nullInterval THEN RETURN;
VM.Unpin[space];
VM.Free[space];
};
Commander.Register["CheckAndInit", CheckAndInit,
"Checks the WalnutDump file and then creates the files for walnut"];
Commander.Register["InitWalnutFiles", InitWalnutFiles, "Create the files for walnut"];
Commander.Register["InitOtherWalnutFiles", DoOtherInit, "Create the files for walnut"];
Commander.Register["JustDoFiles", JustDoFiles, "Create the files for walnut"];
END.