<> <> <> <> <> <> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, Letter, LF, SP, TAB], DebuggerSwap USING [CallDebugger], FS USING [Error, ErrorDesc, Open, OpenFile, StreamOpen], GermSwap USING [switches ], IO USING[ BreakProc, EndOfStream, EraseChar, GetChar, GetTokenRope, int, Put, PutChar, PutF, PutFR, PutRope, STREAM ], Loader USING [Error, ErrorType, Instantiate, IRItem, Start], PrincOps USING [ControlModule, NullControl], Rope USING [ Cat, Concat, Equal, Fetch, Find, FromChar, Index, Length, ROPE, Substr ], SimpleTerminal USING [TurnOff, TurnOn], SystemVersion USING [release], Terminal USING [Select], UserCredentials USING [Get]; LoaderDriver: CEDAR PROGRAM IMPORTS Ascii, DebuggerSwap, FS, GermSwap, IO, Loader, Rope, SimpleTerminal, SystemVersion, Terminal, UserCredentials = BEGIN in, out: IO.STREAM; GetFileStr: PROC[cmName: Rope.ROPE] RETURNS[IO.STREAM] = { RETURN[ FS.StreamOpen[cmName] ] }; 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 = $unknownFile 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, selectOtherTerminal: BOOL _ FALSE]; TryFile: PROC[cmName: Rope.ROPE] RETURNS[done: BOOL _ TRUE] = BEGIN fileStr: IO.STREAM; GetToken: PROC RETURNS[Rope.ROPE] = { RETURN[ fileStr.GetTokenRope[MyBreak].token ] }; 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.selectOtherTerminal _ 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: LIST OF Loader.IRItem; 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 # NIL THEN { out.PutRope[" (has unbound imports: "]; FOR l: LIST OF Loader.IRItem _ unbound, l.rest UNTIL l = NIL DO out.PutChar['[]; out.PutRope[l.first.interfaceName]; out.PutChar[',]; out.Put[IO.int[l.first.index]]; out.PutRope["] "]; ENDLOOP; out.PutRope[")"]; }; EXITS thisFailed => loadFailed _ TRUE; END; ENDLOOP; TRUSTED {IF GermSwap.switches[five] THEN DebuggerSwap.CallDebugger["Key stop 5"L]}; 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.selectOtherTerminal THEN [] _ Terminal.Select[NIL]; 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"]; SimpleTerminal.TurnOff[]; END; EXITS failed => done _ 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, DEL => 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 Ascii.Letter[ch] OR Ascii.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.PutRope[condition]; 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 = IO.PutFR[".%g.%g.%g", [integer[SystemVersion.release.major]], [integer[SystemVersion.release.minor]], [integer[SystemVersion.release.patch]] ]; loadees: Rope.ROPE = Rope.Cat["BasicLoadees", version]; user, briefUser: Rope.ROPE; [in: in, out: out] _ SimpleTerminal.TurnOn[]; out.PutRope["\nThis is the \"Basic\" boot file."]; user _ UserCredentials.Get[].name; briefUser _ user.Substr[0, user.Index[0, "."]]; IF GermSwap.switches[l] AND OptionalCommand["\nDo you want to specify an explicit \"Loadees\" command file? ", "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.