<> <> <> <> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, LF, SP, TAB], DebuggerSwap USING [CallDebugger], File USING [EraseVolume, Error, FindVolumeFromName, Volume], FS USING [Error, ErrorDesc, Open, OpenFile], FSFileStream USING [Create], GermSwap USING [switches ], IO USING[ BreakProc, EndOfStream, EraseChar, GetChar, GetToken, noWhereStream, PutChar, PutF, PutRope, PutToRope, STREAM ], Loader USING [Error, ErrorType, Instantiate, Start], PrincOps USING [ControlModule, NullControl], Rope USING [ Cat, Concat, Digit, Equal, Fetch, Find, FromChar, Index, Length, Letter, ROPE, Substr ], SimpleTerminal USING [TurnOff, TurnOn], SystemVersion USING [release], UserCredentials USING [Get]; LoaderDriver: CEDAR PROGRAM IMPORTS DebuggerSwap, File, FS, FSFileStream, GermSwap, IO, Loader, Rope, SimpleTerminal, SystemVersion, UserCredentials = BEGIN in, out: IO.STREAM; ttyOff: BOOL _ FALSE; GetFileStr: PROC[cmName: Rope.ROPE] RETURNS[IO.STREAM] = <> { RETURN[ FSFileStream.Create[FS.Open[cmName], oldReadOnly] ] }; MyBreak: IO.BreakProc = BEGIN SELECT char FROM Ascii.SP, Ascii.CR, Ascii.TAB, Ascii.LF, ', => RETURN[sepr]; '/ => RETURN[break]; ENDCASE => RETURN[other]; END; PutFSError: PROC[error: FS.ErrorDesc] = BEGIN IF error.code = notFound THEN out.PutRope["not found"] ELSE BEGIN out.PutRope["FS.Error: "]; out.PutRope[IF error.explanation.Length[] # 0 THEN error.explanation ELSE "no message"]; END; END; PutLoaderError: PROC[type: Loader.ErrorType, message: Rope.ROPE] = BEGIN out.PutRope["Loader.Error["]; out.PutRope[SELECT type FROM invalidBcd => "invalid BCD", fileNotFound => "file not found (?)", versionMismatch => "version mismatch", loadStateFull => "loadstate full", insufficientVM => "insufficient VM", ENDCASE => "unknown"]; IF message.Length[] # 0 THEN { out.PutChar[',]; out.PutRope[message] }; out.PutChar[']]; END; Loadee: TYPE = RECORD[ rest: REF Loadee _ NIL, name: Rope.ROPE _ NIL, cm: PrincOps.ControlModule _ PrincOps.NullControl, codeLinks: BOOL _ TRUE, callDebugger: BOOL _ FALSE, turnOffTerminal: BOOL _ FALSE]; TryFile: PROC[cmName: Rope.ROPE] RETURNS[done: BOOL _ TRUE] = BEGIN fileStr: IO.STREAM; GetToken: PROC RETURNS[Rope.ROPE] = { RETURN[ fileStr.GetToken[MyBreak] ] }; loadees: REF Loadee _ NIL; last: REF Loadee _ NIL; loadFailed: BOOL _ FALSE; nextToken: Rope.ROPE _ NIL; out.PutF["\nTrying \"%g\" ... ", [rope[cmName]]]; fileStr _ GetFileStr[cmName ! FS.Error => { PutFSError[error]; GOTO failed }]; out.PutRope["ok\nParsing the command file ... "]; DO -- parse input for each loadee this: REF Loadee _ NEW[Loadee _ [] ]; end: BOOL _ FALSE; swSep: Rope.ROPE = "/"; IF nextToken = NIL THEN nextToken _ GetToken[ ! IO.EndOfStream => EXIT]; IF nextToken.Equal[swSep] THEN end _ TRUE ELSE BEGIN IF last = NIL THEN loadees _ this ELSE last.rest _ this; last _ this; this.name _ nextToken; IF this.name.Find["."] < 0 THEN this.name _ this.name.Cat[".bcd"]; nextToken _ GetToken[ ! IO.EndOfStream => { nextToken _ NIL; CONTINUE }]; END; IF nextToken.Equal[swSep] THEN BEGIN positive: BOOL _ TRUE; nextToken _ GetToken[ ! IO.EndOfStream => { nextToken _ NIL; CONTINUE }]; FOR i: NAT IN NAT[0..nextToken.Length[]) DO SELECT nextToken.Fetch[i] FROM '-, '~ => { positive _ FALSE; LOOP }; 'l, 'L => this.codeLinks _ positive; 'd, 'D => this.callDebugger _ positive; 't, 'T => this.turnOffTerminal _ positive; 'e, 'E => end _ positive; ENDCASE => NULL; positive _ TRUE ENDLOOP; nextToken _ NIL; END; IF end THEN EXIT; ENDLOOP; out.PutRope["ok"]; FOR this: REF Loadee _ loadees, this.rest UNTIL this = NIL DO -- load them BEGIN thisFile: FS.OpenFile; unbound: BOOL; out.PutF["\nLoading \"%g\"", [rope[this.name]] ]; IF NOT this.codeLinks THEN out.PutRope[" (frame links)"]; out.PutRope[" ... "]; thisFile _ FS.Open[this.name ! FS.Error => { PutFSError[error]; GOTO thisFailed } ]; TRUSTED{[cm: this.cm, unboundImports: unbound] _ Loader.Instantiate[file: thisFile, codeLinks: this.codeLinks ! FS.Error => { PutFSError[error]; GOTO thisFailed }; Loader.Error => { PutLoaderError[type, message]; GOTO thisFailed } ]}; out.PutRope["ok"]; IF unbound THEN out.PutRope[" (has unbound imports)"]; EXITS thisFailed => loadFailed _ TRUE; END; ENDLOOP; IF loadFailed THEN out.PutRope["\nThere were loading errors. I'm giving up."] ELSE BEGIN FOR this: REF Loadee _ loadees, this.rest UNTIL this = NIL DO -- start them IF this.turnOffTerminal AND NOT ttyOff THEN { out.PutRope["\nTerminal off.\n"]; SimpleTerminal.TurnOff[]; ttyOff _ TRUE; out _ IO.noWhereStream }; IF this.callDebugger THEN TRUSTED{DebuggerSwap.CallDebugger["/d switch in basic loadees"]}; out.PutF["\nStarting \"%g\" ... ", [rope[this.name]] ]; TRUSTED{Loader.Start[this.cm]}; out.PutRope["ok"]; ENDLOOP; out.PutRope["\nEnd of the command file"]; END; EXITS failed => done _ FALSE; END; TryErase: PROC[name: Rope.ROPE] RETURNS[BOOL] = BEGIN volume: File.Volume = File.FindVolumeFromName[name]; IF volume = NIL THEN { out.PutRope[" ... volume not found"]; RETURN[FALSE] }; out.PutRope[" ... erasing ... "]; File.EraseVolume[volume ! File.Error => BEGIN out.PutRope[SELECT why FROM wentOffline => "volume is offline", nonCedarVolume => "can't erase a non-Cedar volume", inconsistent => "the volume root page seems to be inconsistent", software => "label-check: consult an expert", hardware => "hard disk error: consult an expert", ENDCASE => ERROR]; GOTO bad END ]; out.PutRope["ok"]; RETURN[TRUE] EXITS bad => RETURN[FALSE] END; OptionalCommand: PROC[condition, prompt, default: Rope.ROPE, work: PROC[Rope.ROPE] RETURNS[BOOL]] RETURNS[BOOL] = BEGIN <<"GetID" copied from UserCredentialsImpl>> Rubout: ERROR = CODE; GetID: PROC [default: Rope.ROPE, echo: BOOL _ TRUE] RETURNS [id: Rope.ROPE] = { OPEN Ascii; firstTime: BOOLEAN _ TRUE; c: CHAR; EraseAll: PROC = { IF echo THEN FOR i: INT DECREASING IN [0..id.Length[]) DO out.EraseChar[id.Fetch[i]]; ENDLOOP; id _ NIL; }; Done: PROC [c: CHAR] RETURNS [BOOL] = INLINE { IF firstTime THEN { SELECT c FROM ControlA, BS, ControlQ, ControlW, ControlX, CR, SP => NULL; ENDCASE => EraseAll[]; firstTime _ FALSE; }; RETURN[c = SP OR c = CR] }; id _ default; IF echo THEN out.PutRope[default]; c _ in.GetChar[]; UNTIL Done[c] DO SELECT c FROM DEL => ERROR Rubout; ControlA, BS => { len: INT _ id.Length[]; IF len > 0 THEN { len _ len - 1; IF echo THEN out.EraseChar[id.Fetch[len]]; id _ id.Substr[len: len]; }; }; ControlW, ControlQ => { <, the and following are to be removed.>> alpha: BOOL _ FALSE; FOR i: INT DECREASING IN [0..id.Length[]) DO ch: CHAR = id.Fetch[i]; IF Rope.Letter[ch] OR Rope.Digit[ch] THEN alpha _ TRUE ELSE IF alpha THEN {id _ id.Substr[len: i + 1]; EXIT}; IF echo THEN out.EraseChar[ch]; REPEAT FINISHED => id _ NIL; ENDLOOP; }; ControlX => EraseAll[]; ENDCASE => {id _ id.Concat[Rope.FromChar[c]]; IF echo THEN out.PutChar[c]}; c _ in.GetChar[]; ENDLOOP; }; arg: Rope.ROPE _ default; DO out.PutChar['\n]; out.PutRope[condition]; out.PutRope[" [Y or N]"]; SELECT in.GetChar[] FROM 'y, 'Y, '\n => BEGIN out.PutRope[" yes"]; DO BEGIN ENABLE Rubout => { out.PutRope[" XXX"]; EXIT }; out.PutChar['\n]; out.PutRope[prompt]; out.PutRope[": "]; arg _ GetID[arg]; IF work[arg] THEN RETURN[TRUE]; END ENDLOOP; END; 'n, 'N => { out.PutRope[" no"]; RETURN[FALSE] }; ENDCASE => out.PutRope["\nPlease respond with \"Y\" or \"N\""]; ENDLOOP; END; Main: PROC = BEGIN version: Rope.ROPE = Rope.Cat[ ".", IO.PutToRope[[cardinal[SystemVersion.release.major]]], ".", IO.PutToRope[[cardinal[SystemVersion.release.minor]]], ".", IO.PutToRope[[cardinal[SystemVersion.release.patch]]] ]; loadees: Rope.ROPE = Rope.Cat["BasicLoadees", version]; user: Rope.ROPE = UserCredentials.Get[].name; briefUser: Rope.ROPE = user.Substr[0, user.Index[0, "."]]; [in: in, out: out] _ SimpleTerminal.TurnOn[]; ttyOff _ FALSE; out.PutRope["This is the \"Basic\" boot file."]; IF GermSwap.switches[l] THEN [] _ OptionalCommand["Do you want to erase a volume?", "Volume name", "NucleusTest", TryErase]; IF GermSwap.switches[l] AND OptionalCommand["Do you want to specify an explicit \"Loadees\" command file name?", "Command file name", Rope.Cat["[Ivy]<", briefUser, ">", "UtilityLoadees.cm"], TryFile] THEN NULL ELSE BEGIN out.PutRope["\nLooking for a \"BasicLoadees\" command file."]; IF TryFile[Rope.Cat["[Ivy]<", briefUser, ">", loadees]] OR (NOT GermSwap.switches[d] AND TryFile[Rope.Cat["[Indigo]Top>", loadees]]) OR TryFile[Rope.Cat["[Indigo]Top>", loadees]] OR TryFile[Rope.Cat["[Indigo]Top>", loadees]] THEN NULL ELSE out.PutRope["\nCan't find any command file."]; END; END; Main[]; END.