<> <> <> <> <<>> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, Letter, LF, SP, TAB], Booting USING [switches], DebuggerSwap USING [CallDebugger], FS USING [Error, ErrorDesc, GetInfo, Open, OpenFile, StreamOpen], FSExtras USING [DoInWDir], IO USING [STREAM, BreakProc, Close, EndOfStream, EraseChar, GetChar, GetTokenRope, PutChar, PutF, PutF1, PutRope, int, rope, time], Loader USING [Error, ErrorType, Instantiate, IRItem, Start], PrincOps USING [ControlModule, NullControl], PrinterNames USING [DefaultNames, Get], Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, IsEmpty, Length, ROPE, Substr ], SimpleTerminal USING [TurnOff, TurnOn], SystemNames USING [LocalDir]; PrinterLoaderDriverImpl: CEDAR PROGRAM IMPORTS Ascii, Booting, DebuggerSwap, FS, FSExtras, IO, Loader, PrinterNames, Rope, SimpleTerminal, SystemNames = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; in: STREAM; out: 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 [out: STREAM, error: FS.ErrorDesc] = { IF error.code=$unknownFile THEN out.PutRope[" not found"] ELSE out.PutF[" failed\n FS.Error: %g", IO.rope[error.explanation]]; }; RequiredRope: TYPE ~ ROPE _; RopeFromErrorTypeArray: TYPE ~ ARRAY Loader.ErrorType OF RequiredRope; ropeFromErrorType: REF RopeFromErrorTypeArray ~ NEW[RopeFromErrorTypeArray _ [ invalidBcd: "invalidBcd", fileNotFound: "fileNotFound", versionMismatch: "versionMismatch", loadStateFull: "loadStateFull", insufficientVM: "insufficientVM" ]]; PutLoaderError: PROC [out: STREAM, type: Loader.ErrorType, message: ROPE] = { out.PutF1[" failed\n Loader.Error[%g", IO.rope[ropeFromErrorType[type]]]; IF Rope.IsEmpty[message] THEN out.PutRope["]"] ELSE out.PutF1[", \"%g\"]", IO.rope[message]]; }; Loadee: TYPE = RECORD [ rest: REF Loadee _ NIL, name: ROPE _ NIL, cm: PrincOps.ControlModule _ PrincOps.NullControl, codeLinks: BOOL _ TRUE, callDebugger: 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.PutF1["\nTrying \"%g\" ...", IO.rope[cmName]]; fileStr _ GetFileStr[cmName ! FS.Error => { PutFSError[out, 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; '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; fullFName, attachedTo: ROPE _ NIL; unbound: LIST OF Loader.IRItem; DO out.PutF1["\nLoading %g ...", IO.rope[this.name]]; thisFile _ FS.Open[this.name ! FS.Error => { PutFSError[out, error]; IF Confirm["\n Should this file be tried again? "] THEN LOOP; GOTO thisFailed; } ]; EXIT; ENDLOOP; THROUGH [Rope.Length[this.name]..64) DO out.PutChar['.] ENDLOOP; out.PutF[" %u ...", IO.time[FS.GetInfo[thisFile].created]]; IF NOT this.codeLinks THEN out.PutRope[" (frame links)"]; TRUSTED{[cm: this.cm, unboundImports: unbound] _ Loader.Instantiate[file: thisFile, codeLinks: this.codeLinks ! FS.Error => { PutFSError[out, error]; GOTO thisFailed }; Loader.Error => { PutLoaderError[out, type, message]; GOTO thisFailed } ]}; out.PutRope[" ok"]; IF unbound # NIL THEN { out.PutRope["\n (Unbound imports: "]; FOR l: LIST OF Loader.IRItem _ unbound, l.rest UNTIL l = NIL DO out.PutF["[%g,%g] ", IO.rope[l.first.interfaceName], IO.int[l.first.index]]; ENDLOOP; out.PutRope[")"]; }; EXITS thisFailed => loadFailed _ TRUE; END; ENDLOOP; IF Booting.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.PutF1["\nStarting \"%g\" ... ", IO.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; }; systemDir: ROPE ~ SystemNames.LocalDir["System"]; -- for example, "[]<>7.0>System>" Main: PROC = { remoteNames: PrinterNames.DefaultNames = PrinterNames.Get[]; loadees: ROPE = Rope.Concat[remoteNames.printerType, "Printer.loadees"]; IF Booting.switches[l] AND OptionalCommand[ "\nDo you want to specify an explicit \"Loadees\" command file? ", "Command file name", systemDir.Concat[loadees], TryFile] THEN NULL ELSE { out.PutF1["\nLooking for a \"%g\" command file.", IO.rope[loadees]]; IF TryFile[Rope.Cat[remoteNames.currentSystem, loadees]] THEN NULL ELSE out.PutRope["\nCan't find any command file."]; }; }; [in: in, out: out] _ SimpleTerminal.TurnOn[]; out.PutRope["\nThis is the Printer boot file.\n"]; FSExtras.DoInWDir[systemDir, Main]; END.