DIRECTORY Ascii USING [Digit, Letter, Lower], Booting USING [Boot, BootFileNumber, GetBootFileNumber, MicrocodeType, nullMicrocode, Switches], Buttons USING [Button, ButtonProc, Create], Commander USING [CommandProc, Register], CommandTool USING [ArgumentVector, Failed, Parse], Containers USING [ChildXBound, ChildYBound, Container, Create], Convert USING [CardFromRope, Error], File USING [FindVolumeFromName, GetVolumeName, Handle, NextVolume, PageNumber, SystemVolume, Volume, VolumeID], FileBackdoor USING [IsDebugger], FS USING [EnumerateForNames, Error, NameProc, Open, OpenFile], FSBackdoor USING [GetFileHandle], GermSwap USING [Switch], IO USING [PutFR, PutFR1], Labels USING [Create], List USING [Compare, Comparison, Reverse, Sort], MBQueue USING [Create, CreateButton, Queue, QueueClientAction], MessageWindow USING [Append, Blink, Confirm], PhysicalVolume USING [NextPhysical, Physical, PhysicalInfo, PhysicalRC], Process USING [Detach], Rope USING [Cat, Concat, Equal, FromChar, Length, Map, Match, ROPE, Substr], Rules USING [Create, Rule], SystemVersion USING [machineType], UserProfile USING [Boolean, Line, Token], VFonts USING [FontHeight, StringWidth], ViewerClasses USING [Viewer], ViewerOps USING [AddProp, ChangeColumn, EnumerateViewers, EnumProc, OpenIcon, SetOpenHeight], ViewerSpecs USING [openRightWidth], ViewerTools USING [GetContents, GetSelectedViewer, InhibitUserEdits, MakeNewTextViewer, SelPosRec, SetContents, SetSelection]; BootTool: CEDAR MONITOR IMPORTS Ascii, Booting, Buttons, Commander, CommandTool, Containers, Convert, File, FileBackdoor, FS, FSBackdoor, IO, Labels, List, MBQueue, MessageWindow, PhysicalVolume, Process, Rope, Rules, SystemVersion, UserProfile, VFonts, ViewerOps, ViewerSpecs, ViewerTools = BEGIN ROPE: TYPE = Rope.ROPE; entryHeight: NAT = VFonts.FontHeight[] + 2; entryVSpace: NAT = VFonts.FontHeight[]*2/3; entryHSpace: NAT = 10; maxFeedbackLines: NAT = 5; BootToolHandle: TYPE = REF BootToolHandleRec; BootToolHandleRec: TYPE = RECORD [ outer: Containers.Container _ NIL, height: NAT _ 0, q: MBQueue.Queue _ NIL, bootees: ARRAY BooteeClass OF Bootee _ ALL[NIL], switchesButton: Buttons.Button _ NIL, switches: ViewerClasses.Viewer _ NIL, feedback: ViewerClasses.Viewer _ NIL ]; SimpleButtonRef: TYPE = REF SimpleButtonRec; SimpleButtonRec: TYPE = RECORD [ volume: File.Volume, switches: ROPE ]; q: MBQueue.Queue _ MBQueue.Create[]; initialized: BOOL _ FALSE; simpleButtonsUp: BOOL _ FALSE; -- protected by synchronization on 'q' MakeBootTool: ENTRY Buttons.ButtonProc = { ENABLE UNWIND => NULL; IF mouseButton = blue THEN { handle: BootToolHandle = NEW[BootToolHandleRec _ []]; handle.q _ MBQueue.Create[]; handle.q.QueueClientAction[proc: MakeNewBootTool, data: handle]; } ELSE q.QueueClientAction[proc: MakeSimpleButtons, data: NIL]; }; MakeNewBootTool: PROC [h: REF ANY] = { handle: BootToolHandle = NARROW[h]; handle.outer _ Containers.Create[info: [ name: "Boot Tool", iconic: TRUE, column: right, scrollable: FALSE], paint: FALSE -- keeps icon from appearing ]; ViewerOps.AddProp[handle.outer, $BootToolHandle, handle]; BuildSources[handle]; BuildSwitches[handle]; BuildFeedback[handle]; ViewerOps.SetOpenHeight[handle.outer, handle.height]; ViewerOps.OpenIcon[icon: handle.outer, bottom: FALSE]; }; MakeSimpleButtons: PROC [ignored: REF ANY] = { volume: File.Volume _ NIL; IF simpleButtonsUp THEN RETURN; UNTIL (volume _ File.NextVolume[volume]) = NIL DO name: ROPE _ File.GetVolumeName[volume]; switches: ROPE = GetSwitches[name]; IF switches.Length[] > 0 THEN name _ name.Cat["/", switches]; [] _ q.CreateButton[ info: [name: name], proc: SimpleBootProc, clientData: NEW[SimpleButtonRec _ [volume, switches]] ]; ENDLOOP; IF UserProfile.Boolean["BootTool.Basic", TRUE] THEN [] _ q.CreateButton[info: [name: "Basic"], proc: BasicCedar]; IF UserProfile.Boolean["BootTool.Alto", TRUE] THEN [] _ q.CreateButton[info: [name: "Alto"], proc: AltoBootProc]; simpleButtonsUp _ TRUE; }; GetSwitches: PROC [name: ROPE] RETURNS [switches: ROPE] = { name _ name.Concat["Switches"]; switches _ UserProfile.Line[Rope.Concat["BootTool.", name]]; }; AddSeparatingRule: PROC [handle: BootToolHandle] = { rule: Rules.Rule = Rules.Create[info: [ parent: handle.outer, wy: handle.height, ww: handle.outer.cw, wh: 1 ]]; Containers.ChildXBound[handle.outer, rule]; -- constrain rule to be width of parent handle.height _ handle.height + entryVSpace; -- spacing after rule }; MakeCenteredLabel: PROC [handle: BootToolHandle, name: ROPE] = { [] _ Labels.Create[info: [ name: name, parent: handle.outer, wx: (ViewerSpecs.openRightWidth - VFonts.StringWidth[name])/2, wy: handle.height, wh: entryHeight, border: FALSE ]]; handle.height _ handle.height + entryHeight + entryVSpace; }; BooteeClass: TYPE = {volume, file, ucode, pv, --self,-- bb}; Bootee: TYPE = REF BooteeObject; BooteeObject: TYPE = RECORD [ handle: BootToolHandle, class: BooteeClass, button: Buttons.Button _ NIL, choices: ChoiceList _ NIL, currentChoice: ChoiceList _ NIL, -- currentChoice.first is what's interesting displayedChoice: ViewerClasses.Viewer _ NIL ]; ChoiceList: TYPE = LIST OF REF ANY; ChoiceRef: TYPE = REF ChoiceDesc; ChoiceDesc: TYPE = RECORD [ name: ROPE, value: REF ANY]; VolumeVal: TYPE = REF PaintedVolumeVal; PaintedVolumeVal: TYPE = RECORD[File.Volume]; MicrocodeVal: TYPE = REF Booting.BootFileNumber; PhysicalVolumeVal: TYPE = REF PaintedPhysicalVolumeVal; PaintedPhysicalVolumeVal: TYPE = RECORD[File.VolumeID]; BuildSources: PROC [handle: BootToolHandle] = { MakeBootee: PROC [class: BooteeClass, buttonName: ROPE, buttonProc: Buttons.ButtonProc, choicesProc: PROC [BootToolHandle] RETURNS [ChoiceList], readOnly: BOOL _ FALSE] = { bootee: Bootee _ NEW[BooteeObject _ [handle: handle, class: class]]; doIt: Buttons.Button = handle.q.CreateButton[ info: [name: "Boot", wx: entryHSpace, wy: handle.height, wh: entryHeight, parent: handle.outer, border: TRUE ], proc: DoBoot, clientData: bootee, guarded: TRUE ]; IF buttonProc ~= NIL THEN bootee.button _ Buttons.Create[ info: [ name: buttonName, wx: doIt.wx + doIt.ww, wy: handle.height, wh: entryHeight, parent: handle.outer, border: FALSE ], proc: buttonProc, fork: FALSE, clientData: handle ] ELSE [] _ Labels.Create[ info: [ name: buttonName, wx: doIt.wx + doIt.ww, wy: handle.height, wh: entryHeight, parent: handle.outer, border: FALSE ] ]; IF choicesProc ~= NIL THEN { bootee.displayedChoice _ ViewerTools.MakeNewTextViewer[info: [ parent: handle.outer, wx: bootee.button.wx + bootee.button.ww + entryHSpace, wy: handle.height, ww: 9999, -- arbitrary; Containers.ChildXBound will fix. wh: entryHeight, data: NIL, scrollable: FALSE, border: FALSE ]]; Containers.ChildXBound[container: handle.outer, child: bootee.displayedChoice]; IF readOnly THEN ViewerTools.InhibitUserEdits[bootee.displayedChoice]; bootee.currentChoice _ bootee.choices _ choicesProc[handle]; SetChoice[bootee]; }; handle.bootees[class] _ bootee; handle.height _ handle.height + entryHeight + entryVSpace; }; MakeCenteredLabel[handle, "Booting Sources"]; MakeBootee[volume, "Volume:", SelectVolumeBootee, BuildVolumeList, TRUE]; MakeBootee[file, "File:", SelectFileBootee, BuildBootFileList]; MakeBootee[ucode, "Microcode:", SelectMicrocodeBootee, BuildMicrocodeList]; MakeBootee[ pv, "Physical Volume:", SelectPhysicalVolumeBootee, BuildPhysicalVolumeList, TRUE]; MakeBootee[bb, "Button", NIL, NIL]; }; SetChoice: PROC [bootee: Bootee, deselect: BOOL _ FALSE] = { name: ROPE = IF bootee.currentChoice = NIL THEN NIL ELSE NARROW[bootee.currentChoice.first, ChoiceRef].name; ViewerTools.SetContents[bootee.displayedChoice, name]; IF deselect THEN ViewerTools.SetSelection[bootee.displayedChoice, NEW[ViewerTools.SelPosRec _ [start: Rope.Length[name], length: 0]]]; }; BuildVolumeList: PROC [handle: BootToolHandle] RETURNS [volumes: ChoiceList _ NIL] = { system: File.Volume = File.SystemVolume[]; volume: File.Volume _ NIL; UNTIL (volume _ File.NextVolume[volume]) = NIL DO IF volume ~= system THEN volumes _ CONS[VolumeToChoice[volume], volumes]; ENDLOOP; volumes _ List.Sort[volumes, CompareChoiceNames]; RETURN[CONS[VolumeToChoice[system], volumes]] }; VolumeToChoice: PROC [volume: File.Volume] RETURNS [ChoiceRef] = { RETURN[NEW[ChoiceDesc _ [ File.GetVolumeName[volume], NEW[PaintedVolumeVal _ [volume]] ]]] }; CompareChoiceNames: PROC [ref1, ref2: REF ANY] RETURNS [List.Comparison] = { RETURN[List.Compare[NARROW[ref1, ChoiceRef].name, NARROW[ref2, ChoiceRef].name]]; }; InstallRequest: TYPE = RECORD [bootee: Bootee, list: ChoiceList]; InstallList: PROC [data: REF ANY] = { req: REF InstallRequest = NARROW[data]; req.bootee.choices _ req.list; }; BuildBootFileList: PROC [handle: BootToolHandle] RETURNS [ChoiceList _ NIL] = { TRUSTED{Process.Detach[FORK BuildBootFileListForked[handle]]}; }; BuildBootFileListForked: PROC [handle: BootToolHandle] = { handle.q.QueueClientAction[ proc: InstallList, data: NEW[InstallRequest _ [handle.bootees[file], BuildBootFileListSynchronously[handle]]] ]; }; BuildBootFileListSynchronously: PROC [handle: BootToolHandle] RETURNS [bootFiles: ChoiceList _ NIL] = { pattern: ROPE _ UserProfile.Token[key: "BootTool.BootFilePattern", default: NIL]; IF pattern ~= NIL THEN { DoOneBootFile: FS.NameProc = { bootFiles _ CONS[NEW[ChoiceDesc _ [fullFName, NIL]], bootFiles]; RETURN[TRUE] }; IF NOT Rope.Match["*!*", pattern] THEN pattern _ Rope.Concat[pattern, "!H"]; FS.EnumerateForNames[pattern, DoOneBootFile ! FS.Error => CONTINUE]; }; bootFiles _ LIST[NEW[ChoiceDesc _ ["[]<>BootFile.DontDeleteMe", NIL]]]; RETURN[List.Reverse[bootFiles]] }; BuildMicrocodeList: PROC [handle: BootToolHandle] RETURNS [ChoiceList] = { TRUSTED{Process.Detach[FORK BuildMicrocodeListForked[handle]]}; RETURN[LIST[ NEW[ChoiceDesc _ ["(default)", NEW[Booting.BootFileNumber _ Booting.nullMicrocode]]] ]] }; BuildMicrocodeListForked: PROC [handle: BootToolHandle] = { handle.q.QueueClientAction[ proc: InstallList, data: NEW[InstallRequest _ [ handle.bootees[ucode], BuildMicrocodeListSynchronously[handle] ]] ]; }; BuildMicrocodeListSynchronously: PROC [handle: BootToolHandle] RETURNS [microcode: ChoiceList] = { MakeChoice: PROC [name: Rope.ROPE, type: Booting.MicrocodeType] RETURNS [REF ChoiceDesc] = { RETURN [NEW[ChoiceDesc _ [ name, NEW[Booting.BootFileNumber _ Booting.GetBootFileNumber[type]] ]]] }; microcode _ LIST[ NEW[ChoiceDesc _ ["(default)", NEW[Booting.BootFileNumber _ Booting.nullMicrocode]]], MakeChoice["cedarMesa", cedarMesa], MakeChoice["altoMesa", altoMesa], MakeChoice["lisp", lisp], MakeChoice["smalltalk76", smalltalk76], MakeChoice["smalltalk80", smalltalk80], MakeChoice["pilotMesa", pilotMesa] ]; }; BuildPhysicalVolumeList: PROC [handle: BootToolHandle] RETURNS [physicalVolumes: ChoiceList _ NIL] = { OPEN PV: PhysicalVolume; physical: PV.Physical _ NIL; UNTIL (physical _ PV.NextPhysical[physical]) = NIL DO name: ROPE; status: PV.PhysicalRC; id: File.VolumeID; [rootStatus: status, name: name, id: id] _ PV.PhysicalInfo[physical]; IF status = ok THEN physicalVolumes _ CONS[ NEW[ChoiceDesc _ [name, NEW[PaintedPhysicalVolumeVal _ [id]]]], physicalVolumes ]; ENDLOOP; physicalVolumes _ List.Sort[physicalVolumes, CompareChoiceNames]; }; BuildSwitches: PROC [handle: BootToolHandle] = { help: Buttons.Button; AddSeparatingRule[handle]; MakeCenteredLabel[handle, "Booting Options"]; help _ handle.q.CreateButton[ info: [name: "Help", wx: entryHSpace, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE ], proc: SwitchesHelp, clientData: handle ]; handle.switchesButton _ Buttons.Create[ info: [name: "Switches:", wx: help.wx + help.ww, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: FALSE ], proc: SelectBootSwitches, fork: TRUE, clientData: handle ]; handle.switches _ ViewerTools.MakeNewTextViewer[info: [ parent: handle.outer, wx: handle.switchesButton.wx + handle.switchesButton.ww + entryHSpace, wy: handle.height, ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound wh: entryHeight, data: NIL, -- initial contents scrollable: FALSE, border: FALSE ]]; Containers.ChildXBound[container: handle.outer, child: handle.switches]; ViewerTools.SetContents[handle.switches, GetSwitches[NIL]]; handle.height _ handle.height + entryHeight + entryVSpace; }; BuildFeedback: PROC [handle: BootToolHandle] = { feedbackHeight: NAT = maxFeedbackLines*entryHeight; AddSeparatingRule[handle]; handle.feedback _ ViewerTools.MakeNewTextViewer[info: [ parent: handle.outer, wx: 0, wy: handle.height, ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound wh: feedbackHeight, data: NIL, -- initial contents scrollable: TRUE, border: FALSE ]]; Containers.ChildXBound[container: handle.outer, child: handle.feedback]; Containers.ChildYBound[container: handle.outer, child: handle.feedback]; ViewerTools.InhibitUserEdits[handle.feedback]; handle.height _ handle.height + feedbackHeight + entryVSpace; }; SelectVolumeBootee: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; previousContents: ROPE = ViewerTools.GetContents[handle.bootees[volume].displayedChoice]; ViewerTools.SetSelection[handle.bootees[volume].displayedChoice, NEW[ViewerTools.SelPosRec _ [start: Rope.Length[previousContents], length: 0]]]; AdvanceName[handle.bootees[volume]]; }; SelectFileBootee: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; previouslySelected: BOOL = ViewerTools.GetSelectedViewer[] = handle.bootees[file].displayedChoice; ViewerTools.SetSelection[viewer: handle.bootees[file].displayedChoice, selection: NIL]; IF previouslySelected THEN { IF handle.bootees[file].choices = NIL OR (mouseButton ~= red) THEN handle.bootees[file].choices _ BuildBootFileListSynchronously[handle]; AdvanceName[handle.bootees[file]]; }; }; SelectMicrocodeBootee: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; previouslySelected: BOOL = ViewerTools.GetSelectedViewer[] = handle.bootees[ucode].displayedChoice; ViewerTools.SetSelection[viewer: handle.bootees[ucode].displayedChoice, selection: NIL]; IF previouslySelected THEN AdvanceName[handle.bootees[ucode]]; }; SelectPhysicalVolumeBootee: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; previousContents: ROPE = ViewerTools.GetContents[handle.bootees[pv].displayedChoice]; ViewerTools.SetSelection[handle.bootees[pv].displayedChoice, NEW[ViewerTools.SelPosRec _ [start: Rope.Length[previousContents], length: 0]]]; AdvanceName[handle.bootees[pv]]; }; AdvanceName: PROC [bootee: Bootee] = { choiceRef: ChoiceRef = IF bootee.currentChoice = NIL THEN NIL ELSE NARROW[bootee.currentChoice.first]; IF bootee.choices = NIL THEN RETURN; IF choiceRef = NIL OR ~Rope.Equal[choiceRef.name, ViewerTools.GetContents[bootee.displayedChoice], FALSE] OR (bootee.currentChoice _ bootee.currentChoice.rest) = NIL THEN bootee.currentChoice _ bootee.choices; SetChoice[bootee: bootee, deselect: TRUE]; }; SelectBootSwitches: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; ViewerTools.SetSelection[viewer: handle.switches, selection: NIL]; }; CharFromSwitchArray: TYPE ~ ARRAY GermSwap.Switch OF CHAR; charFromSwitch: REF READONLY CharFromSwitchArray ~ NEW[CharFromSwitchArray ~ [ zero: '0, one: '1, two: '2, three: '3, four: '4, five: '5, six: '6, seven: '7, eight: '8, nine: '9, a: 'A, b: 'B, c: 'C, d: 'D, e: 'E, f: 'F, g: 'G, h: 'H, i: 'I, j: 'J, k: 'K, l: 'L, m: 'M, n: 'N, o: 'O, p: 'P, q: 'Q, r: 'R, s: 'S, t: 'T, u: 'U, v: 'V, w: 'W, x: 'X, y: 'Y, z: 'Z ]]; RopeDefaultNil: TYPE ~ ROPE _ NIL; SwitchTableArray: TYPE ~ ARRAY GermSwap.Switch OF RopeDefaultNil; switchTable: REF READONLY SwitchTableArray ~ NEW[SwitchTableArray ~ [ zero: "worry-call debugger as early as possible", one: "call debugger after MesaRuntime initialization", two: "call debugger after VM initialization", three: "call debugger after File initialization", four: "call debugger after finding VM file and swapping in non-initial parts of boot file", five: "call debugger in LoaderDriver after loading but before starting loadees", a: "don't start Ethernet1 drivers", b: "don't start Ethernet2 drivers", c: "don't start Communication package", d: "development version: don't try the Cedar directory in LoaderDriver/Installer", f: "do full booting sequence, even if defaults would imply a rollback", h: "hang in CPU loop with MP code instead of swapping to debugger", i: "enable control-swat as early as possible (instead of waiting until after login)", l: "long (and interactive) installation dialogue", n: "don't touch the disk during initialization (forces entry to Iago)", p: "don't read user profile from disk", q: "quick: don't check for essential DF files", r: "rollback if the system volume has a valid checkpoint", t: "force teledebugging instead of disk world-swap", v: "perform VM copying for checkpoint (internal use only!)", w: "for world-swap debugger, assume debuggee outload file is valid/interesting" ]]; SwitchesHelp: Buttons.ButtonProc = { handle: BootToolHandle = NARROW[clientData]; r: ROPE _ "Switch interpretations:\N"; textSwitches: ROPE = ViewerTools.GetContents[handle.switches]; switches: Booting.Switches _ ALL[TRUE]; all: BOOL = textSwitches.Length[] = 0; IF ~all THEN { error: ROPE; [switches, error] _ RopeToSwitches[textSwitches]; IF error.Length[] > 0 THEN {ViewerTools.SetContents[handle.feedback, error]; RETURN}; }; FOR sw: GermSwap.Switch IN GermSwap.Switch DO IF switches[sw] THEN { expl: ROPE ~ switchTable[sw]; r _ r.Concat[IO.PutFR[" %g - %g\n", [character[charFromSwitch[sw]]], [rope[IF expl.Length[]>0 THEN expl ELSE "has no assigned interpretation"]] ]]; }; ENDLOOP; ViewerTools.SetContents[handle.feedback, r]; }; PostMessage: PROC [handle: BootToolHandle, msg: ROPE] = { IF handle = NIL THEN MessageWindow.Append[msg, TRUE] ELSE ViewerTools.SetContents[handle.feedback, msg]; }; RollbackButton: Buttons.ButtonProc = { PostMessage[NIL, Booting.Boot[[self[]], [r: TRUE]]]; }; SimpleBootProc: Buttons.ButtonProc = { data: SimpleButtonRef = NARROW[clientData]; bootSwitches: Booting.Switches; error: ROPE; IF SomethingWorthSaving[] THEN RETURN; [bootSwitches, error] _ RopeToSwitches[data.switches]; IF mouseButton = blue THEN bootSwitches[f] _ TRUE; IF error = NIL THEN error _ Booting.Boot[[logical[data.volume]], bootSwitches]; PostMessage[NIL, error] }; AltoBootProc: Buttons.ButtonProc = { IF ~SomethingWorthSaving[] THEN PostMessage[NIL, Booting.Boot[[microcode[Booting.GetBootFileNumber[altoMesa]]], []]]; }; BasicCedar: Buttons.ButtonProc = { file: File.Handle = DirLookup[bootFileName]; IF file ~= NIL THEN { switches: Booting.Switches; error: ROPE; [switches, error] _ RopeToSwitches[UserProfile.Line["BootTool.FileSwitches", "f"]]; IF error = NIL AND ~SomethingWorthSaving[] THEN error _ Booting.Boot[[file[file]], switches]; PostMessage[NIL, error]; } ELSE PostMessage[NIL, Rope.Concat[bootFileName, " not found."]]; }; DoBoot: Buttons.ButtonProc = { CantBoot: ERROR = CODE; DoBootInner: PROC = --INLINE-- { bootee: Bootee = NARROW[clientData]; handle: BootToolHandle = bootee.handle; isCurrentChoice: BOOL = bootee.currentChoice ~= NIL AND Rope.Equal[ ViewerTools.GetContents[bootee.displayedChoice], NARROW[bootee.currentChoice.first, ChoiceRef].name]; choiceRef: ChoiceRef = IF isCurrentChoice THEN NARROW[bootee.currentChoice.first] ELSE NIL; ProcessOptions: PROC RETURNS [switches: Booting.Switches] = { error: ROPE; [switches, error] _ RopeToSwitches[ViewerTools.GetContents[handle.switches]]; IF error ~= NIL THEN {PostMessage[handle, error]; ERROR CantBoot}; }; SELECT bootee.class FROM volume => { IF ~isCurrentChoice THEN ERROR; PostMessage[handle, Booting.Boot[[logical[NARROW[choiceRef.value, VolumeVal]^]], ProcessOptions[]]]; }; file => { file: File.Handle; name: ROPE = IF isCurrentChoice THEN choiceRef.name ELSE ViewerTools.GetContents[bootee.displayedChoice]; IF (file _ DirLookup[name]) = NIL THEN { PostMessage[handle, Rope.Cat["Unrecognizable file name '", name, "'"]]; ERROR CantBoot; }; PostMessage[handle, Booting.Boot[[file[file]], ProcessOptions[]]]; }; ucode => { bfn: Booting.BootFileNumber; IF isCurrentChoice THEN bfn _ NARROW[choiceRef.value, MicrocodeVal]^ ELSE { r: ROPE = ViewerTools.GetContents[bootee.displayedChoice]; bfnC: LONG CARDINAL = Convert.CardFromRope[r ! Convert.Error => GO TO badBfn]; IF bfnC > CARDINAL.LAST--Booting.BootFileNumber-- THEN GO TO badBfn; bfn _ [bfnC]; -- can't cause a fault EXITS badBfn => { PostMessage[handle, "Bad microcode specification"]; ERROR CantBoot; }; }; PostMessage[handle, Booting.Boot[[microcode[bfn]], ProcessOptions[]]]; }; pv => { IF ~isCurrentChoice THEN ERROR; PostMessage[handle, Booting.Boot[ [physical[NARROW[choiceRef.value, PhysicalVolumeVal]^]], ProcessOptions[]]]; }; bb => { PostMessage[handle, Booting.Boot[[bootButton[]], ProcessOptions[]]]; }; ENDCASE; }; IF SomethingWorthSaving[] THEN RETURN; DoBootInner[ ! CantBoot => CONTINUE]; }; DirLookup: PROC [fileName: ROPE] RETURNS [file: File.Handle _ NIL] = { file _ FSBackdoor.GetFileHandle[FS.Open[fileName ! FS.Error => CONTINUE]]; IF file = NIL THEN { fileName _ Rope.Concat["///", fileName]; file _ FSBackdoor.GetFileHandle[FS.Open[fileName ! FS.Error => CONTINUE]]; }; }; RopeToSwitches: PROC [textSwitches: ROPE, init: Booting.Switches _ []] RETURNS [switches: Booting.Switches, error: ROPE _ NIL] = { errors: ROPE _ NIL; SetBootSwitch: PROC [c: CHAR] RETURNS [stop: BOOL _ FALSE] = { SELECT TRUE FROM Ascii.Letter[c] => switches[VAL[GermSwap.Switch.a.ORD+(Ascii.Lower[c]-'a)]] _ TRUE; Ascii.Digit[c] => switches[VAL[GermSwap.Switch.zero.ORD+(c-'0)]] _ TRUE; ENDCASE => errors _ errors.Concat[Rope.FromChar[c]]; }; switches _ init; [] _ Rope.Map[base: textSwitches, action: SetBootSwitch]; IF errors.Length[] > 0 THEN error _ Rope.Concat["Unrecognizable boot switch(es): ", errors]; }; SomethingWorthSaving: PROC RETURNS [BOOL] = { dirty: BOOL _ FALSE; CheckDirty: ViewerOps.EnumProc = { IF v.newVersion OR v.newFile THEN dirty _ TRUE ELSE RETURN; IF v.offDeskTop THEN ViewerOps.ChangeColumn[v, left]; }; ViewerOps.EnumerateViewers[CheckDirty]; IF dirty THEN { MessageWindow.Blink[]; IF ~MessageWindow.Confirm["Confirm discard of edits . . . "] THEN { MessageWindow.Append["boot aborted."]; RETURN[TRUE] }; }; RETURN[FALSE] }; BootTheFile: PROC [name: ROPE, switches: Booting.Switches] RETURNS [error: ROPE _ NIL] = { file: File.Handle = DirLookup[name]; IF file # NIL THEN { switches _ RopeToSwitches[UserProfile.Line["BootTool.FileSwitches", "f"], switches].switches; error _ Booting.Boot[ [file[file]], switches ]; }; }; BootCommand: Commander.CommandProc = { argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; switches: Booting.Switches _ []; IF SomethingWorthSaving[] THEN { msg _ "Boot not allowed, something needs saving."; GO TO oops; }; FOR i: NAT IN [1..argv.argc) DO arg: ROPE _ argv[i]; IF Rope.Match["-*", arg] THEN { [switches, msg] _ RopeToSwitches[Rope.Substr[arg, 1], switches]; IF msg # NIL THEN GO TO oops; LOOP; }; SELECT TRUE FROM Rope.Equal[arg, "Alto", FALSE] => { AltoBootProc[NIL]; }; Rope.Equal[arg, "Basic", FALSE] => { IF (msg _ BootTheFile[bootFileName, switches]) # NIL THEN GO TO oops; msg _ Rope.Concat[bootFileName, " not found"]; GO TO oops; }; (msg _ BootTheFile[arg, switches]) # NIL => GO TO oops; ENDCASE => { volume: File.Volume _ File.FindVolumeFromName[arg]; IF volume # NIL THEN { msg _ Booting.Boot[[logical[volume]], switches]; GO TO oops; }; msg _ IO.PutFR1["Boot not performed, %g not found.", [rope[arg]]]; GO TO oops; }; ENDLOOP; msg _ Booting.Boot[ [self[]], switches ]; GO TO oops; EXITS oops => result _ $Failed; }; Initialize: ENTRY PROC = { ENABLE UNWIND => NULL; IF initialized THEN RETURN; IF ~FileBackdoor.IsDebugger[File.SystemVolume[]] THEN { [] _ q.CreateButton[ info: [name: "RollBack"], proc: RollbackButton, fork: TRUE, guarded: TRUE]; }; [] _ Buttons.Create[info: [name: "Boot"], proc: MakeBootTool, fork: TRUE]; Commander.Register["///Commands/Boot", BootCommand, "Boot command"]; initialized _ TRUE; }; OpenBootTool: Commander.CommandProc = { MakeBootTool[NIL, NIL, blue, FALSE, FALSE]; }; bootFileName: ROPE _ SELECT SystemVersion.machineType FROM dolphin => "BasicCedarD0.boot", dorado => "BasicCedarDorado.boot", dandelion => "BasicCedarDLion.boot", dicentra => "BasicCedarDicentra.boot", ENDCASE => "BasicCedar.boot"; Initialize[]; Commander.Register["///Commands/BootTool", OpenBootTool, "creates a Boot Tool"]; END. BootTool.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) June 19, 1985 8:19:29 pm PDT Doug Wyatt, March 6, 1985 5:25:49 pm PST Global Variables (monitored) [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] In order to ensure that some reference to the main data structure exists, we store a reference to it on the property list of the outer container. It would be nicer if containers had a clientData field for this purpose. The following turns off pending delete, and sets the type-in point at the end of the text if user editing is not inhibited [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] Kill pending delete selection elsewhere in tool [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] Kill pending delete selection elsewhere in tool [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] This procedure can't be INLINE until Trinity (catch phrase in caller). There is a boot file specified, so boot it [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] This is a switch, so convert the character switches to boot switches If the argument is a file name, then try to boot it. This may be a volume name, so try to boot it. [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] Κ·– "Cedar" style˜codešœ ™ Kšœ Οmœ1™Kšœ žœ˜!Kšœ žœ ˜Kšžœžœ˜Kšœžœ ˜Kšœžœ&˜0Kšœžœ2˜?Kšœžœ˜-Kšœžœ4˜HKšœžœ ˜Kšœžœ4žœ ˜LKšœžœ˜Kšœžœ˜"Kšœ žœ˜)Kšœžœ˜'Kšœžœ ˜Kšœ žœP˜_Kšœ žœ˜#Kšœ žœm˜~—K˜šΠblœžœž˜Kšžœ[žœžœ•˜‰Kšœž˜—˜Kšžœžœžœ˜K˜Kšœ žœ˜+Kšœ žœ˜+Kšœ žœ˜Kšœžœ˜K˜Kšœžœžœ˜-K˜šœžœžœ˜"Kšœžœ˜"Kšœžœ˜Kšœžœ˜Kš œ žœ žœ žœžœ˜0Kšœ!žœ˜%Kšœ!žœ˜%Kšœ!ž˜$K˜—K˜Kšœžœžœ˜,K˜šœžœžœ˜ Kšœ˜Kšœ ž˜K˜K˜——™K˜Kšœ$˜$K˜Kšœ žœžœ˜K˜KšœžœžœΟc&˜FK˜K˜—šœžœ˜*KšΠck~™~Kšžœžœžœ˜šžœžœ˜Kšœžœ˜5K˜Kšœ@˜@Kšœ˜—Kšžœ4žœ˜=K˜—K˜šΟnœžœžœžœ˜&Kšœžœ˜#˜(Kšœžœžœ˜CKšœžœ ˜*Kšœ˜—KšœΫ™ΫKšœ9˜9K˜K˜K˜Kšœ5˜5Kšœ/žœ˜6K˜K˜—š’œžœ žœžœ˜.Kšœžœ˜Kšžœžœžœ˜šžœ&žœž˜1Kšœžœ˜(Kšœ žœ˜#Kšžœžœ ˜=šœ˜Kšœ)˜)Kšœ žœ&˜5Kšœ˜—Kšžœ˜—šžœ'žœž˜3Kšœ=˜=—šžœ&žœž˜2Kšœ>˜>—Kšœžœ˜Kšœ˜K˜—š ’ œžœžœžœ žœ˜;Kšœ˜Kšœ<˜K˜K˜Kšœž˜ Kšœ˜—K˜:K˜K˜—Kšœ žœ  œ˜K˜K˜6K˜Kšœ  .˜9K˜Kšœžœ˜ Kšœ žœ˜Kšœž˜ Kšœ˜—K˜OKšžœ žœ6˜FKšœ<˜Kšœ˜—K˜š’œžœ˜:šœ˜Kšœ˜KšœžœQ˜ZK˜—K˜K˜—š’œžœžœžœ˜gK•StartOfExpansion$[key: ROPE, default: ROPE _ NIL]šœ žœ?žœ˜Qšžœ žœžœ˜šœžœ ˜Kšœ žœžœžœ˜@Kšžœžœ˜ K˜—Kšžœžœžœ&˜LKšžœ,žœ žœ˜DK˜—Kšœ žœžœ,žœ˜GKšžœ˜Kšœ˜K˜—š’œžœžœ˜JKšžœžœ$˜?šžœžœ˜ Kšžœžœ2˜TKšœ˜—K˜—K˜š’œžœ˜;šœ˜Kšœ˜šœžœ˜Kšœ˜Kšœ'˜'Kšœ˜—K˜—K˜—K˜š’œžœžœ˜bš ’ œžœ žœžœžœ˜\šžœžœ˜Kšœ˜Kšžœ:˜=K˜—Kšœ˜—šœ žœ˜Kšžœžœ3˜UKšœ#˜#Kšœ!˜!Kšœ˜Kšœ'˜'Kšœ'˜'Kšœ"˜"K˜—Kšœ˜K˜—š’œžœžœ žœ˜fKšžœžœ˜Kšœ žœ žœ˜šžœ žœžœž˜5Kšœžœ˜ Kšœžœ ˜K˜Kšœ+žœ˜Ešžœ ž˜šœžœ˜Kšžœžœ$˜?Kšœ˜Kšœ˜——Kšžœ˜—KšœA˜AK˜—K˜š’ œžœ˜0Kšœ˜K˜K˜-šœ˜˜K˜K˜Kšœ 4˜EK˜Kšœž˜ K˜—K˜K˜K˜—˜'˜Kšœ˜K˜Kšœ 4˜EK˜Kšœž˜ K˜—K˜Kšœžœ˜ K˜K˜—˜7K˜K˜FK˜Kšœ  =˜FK˜Kšœžœ ˜Kšœ žœ˜Kšœž˜ Kšœ˜—K˜HKšœ5žœ˜;K˜:K˜K˜—š’ œžœ˜0Kšœžœ ˜3K˜˜7K˜K˜K˜Kšœ  =˜FKšœ˜Kšœžœ ˜Kšœ žœ˜Kšœž˜ Kšœ˜—K˜HK˜HKšœ.˜.Kšœ=˜=K˜K˜—šœ*˜*Kš‘~™~Kšœžœ ˜,KšœžœC˜YKšœ/™/˜@KšžœM˜P—Kšœ$˜$K˜K˜—šœ(˜(Kš‘~™~Kšœžœ ˜,šœžœ˜KšœG˜G—KšœRžœ˜Wšžœžœ˜šžœ žœžœž˜BK˜F—Kšœ"˜"K˜—K˜K˜—šœ-˜-Kš‘~™~Kšœžœ ˜,šœžœ˜KšœH˜H—KšœSžœ˜XKšžœžœ$˜>K˜K˜—šœ2˜2Kš‘~™~Kšœžœ ˜,Kšœžœ?˜UKšœ/™/šœ<˜Kšœžœžœ˜'Kšœžœ˜&šžœžœ˜Kšœžœ˜ Kšœ1˜1Kšžœžœ3žœ˜UK˜—šžœžœž˜-šžœžœ˜Kšœžœ˜šœ žœ˜$Kšœ ˜ Kšœžœžœžœ#˜JKšœ˜—K˜—Kšžœ˜—Kšœ,˜,K˜—K˜š’ œžœžœ˜9Kšžœ žœžœžœ˜4Kšžœ/˜3K˜—K˜šœ&˜&Kš‘~™~Kšœ žœžœ˜4Kšœ˜K˜—šœ&˜&Kš‘~™~Kšœžœ ˜+K˜Kšœžœ˜ Kšžœžœžœ˜&K˜6Kšžœžœžœ˜2Kšžœ žœžœ<˜OKšœ žœ˜K˜K˜—–‚ -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]šœ$˜$Kš‘~™~šžœž˜Kšœ žœF˜U—K˜K˜—šœ"˜"Kš‘~™~Kšœ,˜,šžœ ž˜šžœ˜K˜Kšœžœ˜ KšœS˜Sšžœ žœžœž˜/Kšœ-˜-—Kšœ žœ ˜K˜—Kšžœ žœ,˜@—K˜—K˜šœ˜Kš‘~™~Kšœ žœžœ˜š’ œžœ  œ˜ KšœF™FKšœžœ ˜$Kšœ'˜'šœžœžœž˜7˜ K˜0Kšžœ.˜4——˜Kš žœžœžœžœžœ˜D—š’œžœžœ!˜=Kšœžœ˜ KšœM˜MKšžœ žœžœžœ ˜BK˜—šžœž˜˜ Kšžœžœžœ˜šœ˜Kšœžœ4˜P—K˜—˜ Kšœ˜šœžœ˜ Kšžœžœ˜&Kšžœ1˜5—šžœžœžœ˜(KšœG˜GKšžœ ˜K˜—KšœB˜BK˜—˜ K˜šžœž˜Kšœžœ ˜,—šžœ˜Kšœžœ3˜:Kš œžœžœ-žœžœ ˜NKš žœžœž œžœžœžœ˜DKšœ ˜$šž˜˜ K˜3Kšžœ ˜K˜——K˜—KšœF˜FK˜—˜Kšžœžœžœ˜šœ!˜!Kšœ žœ<˜L—K˜—˜KšœD˜DK˜—Kšžœ˜—K˜—Kšžœžœžœ˜&Kšœžœ˜%K˜K˜—š ’ œžœ žœžœžœ˜FKšœ žœžœ žœ˜Jšžœžœžœ˜Kšœ(˜(Kšœ žœžœ žœ˜JK˜—K˜—K˜š ’œžœžœžœ%žœžœ˜‚Kšœžœžœ˜š ’ œžœžœžœžœžœ˜>šžœžœž˜Kšœžœžœžœ˜SKšœžœžœ žœ˜HKšžœ-˜4—K˜—Kšœ˜Kšœ9˜9KšžœžœA˜\K˜—K˜š’œžœžœžœ˜-Kšœžœžœ˜šœ"˜"Kš žœžœ žœ žœžœžœ˜;Kšžœžœ!˜5Kšœ˜—K˜'šžœžœ˜K˜šžœ;žœ˜CK˜&Kšžœžœ˜ Kšœ˜—Kšœ˜—Kšžœžœ˜ Kšœ˜K˜—š ’ œžœžœžœ žœžœ˜ZKšœ$˜$šžœžœžœ˜Kšœ*™*Kšœ]˜]Kšœ/˜/K˜—K˜K˜—–L -- [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]˜&Kš‘H™Hšœ8˜8Kšœ)žœžœ˜6—Kšœ ˜ šžœžœ˜ K˜2Kšžœžœ˜ K˜—šžœžœžœž˜Kšœžœ ˜–[]šžœžœ˜KšœD™DKšœ@˜@Kš žœžœžœžœžœ˜Kšžœ˜K˜—šžœžœž˜šœžœ˜#Kšœ žœ˜K˜—šœžœ˜$Kš žœ/žœžœžœžœ˜EKšœ.˜.Kšžœžœ˜ K˜—šœ%žœžœžœ˜7Kšœ4™4—šžœ˜ Kšœ-™-Kšœ3˜3šžœ žœžœ˜Kšœ0˜0Kšžœžœ˜ K˜—Kšœžœ:˜BKšžœžœ˜ K˜——Kšžœ˜—Kšœ)˜)Kšžœžœ˜ Kšžœ˜K˜K˜—š’ œžœžœ˜Kšžœžœžœ˜Kšžœ žœžœ˜šžœ/žœ˜7šœ˜Kšœ6žœ žœ˜K—K˜—KšœDžœ˜JKšœD˜DKšœžœ˜Kšœ˜K˜—–L -- [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]šΟb œ˜'Kš‘H™HKš œ žœžœžœžœ˜+K˜K˜—šœžœžœž˜:Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ&˜&Kšžœ˜K˜—Kšœ ˜ K˜KšœP˜PK˜Kšžœ˜—…—`…ι