<> <> <> <> 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]; CreateWalnutWorld: CEDAR PROGRAM IMPORTS AlpineCmds, AlpineFS, Commander, CommandTool, Convert, File, FS, FSBackdoor, IagoOps, IO, RefText, Rope, UserCredentials, VM, WalnutRoot, WalnutSchema, WalnutStream = BEGIN <> STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; defaultServer: ROPE = "Luther"; serverRope: ROPE = "\001ServerAndDirectory\002"; serverLen: NAT = Rope.Length[serverRope]; RootEntry: TYPE = WalnutKernelDefs.RootEntry; LogInfoFromRoot: TYPE = WalnutKernelDefs.LogInfoFromRoot; field1: REF TEXT = NEW[TEXT[RefText.line]]; <> 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] = { <> 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 = { <> <> 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]; EXITS done => EXIT; 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] }; <<>> <> 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; <> 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.