<> <> <> <> <> <> <> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, Letter, LF, SP, TAB], DebuggerSwap USING [CallDebugger], DefaultRemoteNames USING [DefaultNames, Get], FS USING [Error, ErrorDesc, Open, OpenFile, StreamOpen], GermSwap USING [switches ], IO USING[ BreakProc, Close, 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], <> UserCredentials USING [Get]; LoaderDriver: CEDAR PROGRAM IMPORTS Ascii, DebuggerSwap, DefaultRemoteNames, FS, GermSwap, IO, Loader, Rope, SimpleTerminal, SystemVersion, UserCredentials = BEGIN ROPE: TYPE = Rope.ROPE; in: IO.STREAM; out: IO.STREAM; GetFileStr: PROC[cmName: ROPE] RETURNS[IO.STREAM] = { RETURN[ FS.StreamOpen[cmName] ]; }; MyBreak: IO.BreakProc = { SELECT char FROM Ascii.SP, Ascii.CR, Ascii.TAB, Ascii.LF, ', => RETURN[sepr]; '/ => RETURN[break]; ENDCASE => RETURN[other]; }; GetToken: PROC [stream: IO.STREAM] RETURNS[token: ROPE _ NIL] = { token _ IO.GetTokenRope[stream, MyBreak !IO.EndOfStream => CONTINUE].token; }; PutFSError: PROC[error: FS.ErrorDesc] = { IF error.code = $unknownFile THEN out.PutRope["not found"] ELSE { out.PutRope["FS.Error: "]; out.PutRope[IF error.explanation.Length[] # 0 THEN error.explanation ELSE "no message"]; }; }; PutLoaderError: PROC[type: Loader.ErrorType, message: ROPE] = { 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[']]; }; Loadee: TYPE = RECORD[ rest: REF Loadee _ NIL, name: ROPE _ NIL, cm: PrincOps.ControlModule _ PrincOps.NullControl, codeLinks: BOOL _ TRUE, callDebugger: BOOL _ FALSE, selectOtherTerminal: BOOL _ FALSE]; TryFile: PROC[cmName: ROPE] RETURNS[done: BOOL _ TRUE] = { fileStr: IO.STREAM; loadees: REF Loadee _ NIL; last: REF Loadee _ NIL; loadFailed: BOOL _ FALSE; nextToken: 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 = "/"; IF nextToken = NIL THEN IF (nextToken _ GetToken[fileStr]) = NIL THEN EXIT; IF nextToken.Equal[swSep] THEN end _ TRUE ELSE { 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[fileStr]; }; IF nextToken.Equal[swSep] THEN { positive: BOOL _ TRUE; nextToken _ GetToken[fileStr]; 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; }; IF end THEN EXIT; ENDLOOP; out.PutRope["ok"]; IO.Close[fileStr]; FOR this: REF Loadee _ loadees, this.rest UNTIL this = NIL DO <> 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)"]; DO out.PutRope[" ... "]; thisFile _ FS.Open[this.name ! FS.Error => { PutFSError[error]; IF Confirm["\nShould this file be tried again? "] THEN LOOP; GOTO thisFailed; } ]; EXIT; ENDLOOP; 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 { FOR this: REF Loadee _ loadees, this.rest UNTIL this = NIL DO <> <> 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[]; }; EXITS failed => done _ FALSE; }; OptionalCommand: PROC [condition, prompt, default: ROPE, work: PROC[ROPE] RETURNS[BOOL]] RETURNS[BOOL] = { <<"GetID" copied from UserCredentialsImpl>> Rubout: ERROR = CODE; GetID: PROC [default: ROPE, echo: BOOL _ TRUE] RETURNS [id: ROPE] = { OPEN Ascii; firstTime: BOOL _ 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 _ 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; }; Confirm: PROC [question: ROPE] RETURNS [BOOL] = { DO out.PutRope[question]; SELECT in.GetChar[] FROM 'y, 'Y, '\n => { out.PutRope[" yes"]; RETURN[TRUE]; }; 'n, 'N => { out.PutRope[" no"]; RETURN[FALSE]; }; ENDCASE => out.PutRope["\nPlease respond with \"Y\" or \"N\""]; ENDLOOP; }; Main: PROC = { remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[]; version: ROPE = IO.PutFR["%g.%g", [integer[SystemVersion.release.major]], [integer[SystemVersion.release.minor]] ]; loadees: ROPE = "Basic.Loadees"; user, briefUser, userDir: ROPE _ NIL; [in: in, out: out] _ SimpleTerminal.TurnOn[]; out.PutRope["\nThis is the Basic boot file.\n"]; user _ UserCredentials.Get[].name; briefUser _ user.Substr[0, user.Index[0, "."]]; userDir _ IO.PutFR["%g<%g>%g>", [rope[remoteNames.userHost]], [rope[briefUser]], [rope[version]] ]; IF GermSwap.switches[l] AND OptionalCommand[ "\nDo you want to specify an explicit \"Loadees\" command file? ", "Command file name", Rope.Concat[userDir, "UtilityLoadees.cm"], TryFile] THEN NULL ELSE { out.PutRope["\nLooking for a \"BasicLoadees\" command file."]; IF TryFile[Rope.Concat[userDir, loadees]] OR TryFile[Rope.Cat[remoteNames.current, "Top>", loadees]] THEN NULL ELSE out.PutRope["\nCan't find any command file."]; }; }; Main[]; END.