DIRECTORY AMBridge USING [TVToCardinal], AMTypes USING [--Error,-- Index, IndexToName, NValues, Type, Value], Ascii USING [Digit, Letter, Lower, SP, Upper], BasicTime USING [Now], Booting USING [ Boot, BootFileNumber, Checkpoint, GetBootFileNumber, MicrocodeType, nullMicrocode, Switches], Buttons USING [Button, ButtonProc, Create], Containers USING [ChildXBound, ChildYBound, Container, Create], Convert USING [CardFromRope, Error], File USING [ GetVolumeName, Handle, IsDebugger, NextVolume, PageNumber, SystemVolume, Volume, VolumeID], FS USING [EnumerateForNames, Error, NameProc, Open, OpenFile], FSBackdoor USING [GetFileHandle], GermSwap USING [Switch], IO USING [PutFR, time], 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, Fetch, FromChar, Length, Map, ROPE, Substr], Rules USING [Create, Rule], UserProfile USING [Line], 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 AMBridge, AMTypes, Ascii, BasicTime, Booting, Buttons, Containers, Convert, File, FS, FSBackdoor, IO, Labels, List, MBQueue, MessageWindow, PhysicalVolume, Process, Rope, Rules, 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; [] _ q.CreateButton[info: [name: "Basic"], proc: BasicCedarDorado]; [] _ q.CreateButton[info: [name: "Alto"], proc: AltoBootProc]; [] _ q.CreateButton[info: [name: "BB"], proc: BootButtonProc]; simpleButtonsUp _ TRUE; }; GetSwitches: PROC [name: ROPE] RETURNS [switches: ROPE] = { name _ name.Concat["Switches"]; switches _ UserProfile.Line[name]; IF switches.Length[] = 0 THEN switches _ UserProfile.Line[Rope.Concat["Boot.", 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] = { GetWorkingDir: PROC RETURNS [ROPE _ NIL] = { r: ROPE = ViewerTools.GetContents[handle.bootees[file].displayedChoice]; IF r.Length[] > 0 THEN SELECT r.Fetch[r.Length[]-1] FROM '>, '/ => RETURN[IF r.Fetch[0] = '< THEN Rope.Concat["[]", r] ELSE r]; ENDCASE; }; DoBootFilesWithPrefix: PROC [wDir: ROPE] = { DoOneBootFile: FS.NameProc = { bootFiles _ CONS[NEW[ChoiceDesc _ [fullFName, NIL]], bootFiles]; RETURN[TRUE] }; FS.EnumerateForNames["*.boot!H", DoOneBootFile, wDir ! FS.Error => CONTINUE]; }; wDir: ROPE = GetWorkingDir[]; IF wDir ~= NIL THEN DoBootFilesWithPrefix[wDir] ELSE FOR c: ChoiceList _ handle.bootees[volume].choices, c.rest UNTIL c = NIL DO DoBootFilesWithPrefix[Rope.Cat["[]<", NARROW[c.first, ChoiceRef].name, ">"]]; ENDLOOP; 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] = { OPEN B: Booting; microcode _ LIST[NEW[ChoiceDesc _ ["(default)", NEW[B.BootFileNumber _ B.nullMicrocode]]]]; BEGIN ENABLE --AMTypes.Error-- ANY => CONTINUE; Massage: PROC [name: ROPE] RETURNS [newName: ROPE] = { start: INT _ 1; doingLower: BOOL _ FALSE; newName _ Rope.FromChar[Ascii.Upper[name.Fetch[0]]]; DO next: INT _ start; UNTIL next = name.Length[] OR (doingLower ~= (name.Fetch[next] IN ['a..'z])) DO next _ next + 1; ENDLOOP; newName _ newName.Concat[name.Substr[start, next - start]]; IF next = name.Length[] THEN EXIT ELSE start _ next; IF doingLower THEN newName _ newName.Concat[Rope.FromChar[Ascii.SP]]; doingLower _ ~doingLower; ENDLOOP; }; uT: AMTypes.Type = LOOPHOLE[CODE[B.MicrocodeType]]; FOR i: AMTypes.Index IN NAT[1..AMTypes.NValues[uT]] DO name: ROPE = Massage[AMTypes.IndexToName[uT, i]]; ucodeType: B.MicrocodeType; TRUSTED{ucodeType _ LOOPHOLE[AMBridge.TVToCardinal[AMTypes.Value[uT, i]]]}; microcode _ CONS[ NEW[ChoiceDesc _ [name, NEW[B.BootFileNumber _ B.GetBootFileNumber[ucodeType]]]], microcode]; ENDLOOP; END; RETURN[List.Reverse[microcode]] }; 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, UserProfile.Line["Boot.Switches"]]; 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]; }; SwitchesHelp: Buttons.ButtonProc = { ToChar: PROC [sw: GermSwap.Switch] RETURNS [ROPE] = { IF sw IN [zero..nine] THEN RETURN [Rope.FromChar['0+sw.ORD]]; RETURN[Rope.FromChar['A + (sw.ORD - GermSwap.Switch.a.ORD)]] }; 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 IF switchTable[sw].Length[] > 0 THEN r _ r.Cat[" ", ToChar[sw], " - ", switchTable[sw], "\N"] ELSE IF ~all THEN r _ r.Cat[" ", ToChar[sw], " - ", "has no assigned interpretation\N"]; ENDLOOP; ViewerTools.SetContents[handle.feedback, r]; }; RopeDefaultNil: TYPE = ROPE _ NIL; switchTable: ARRAY GermSwap.Switch OF RopeDefaultNil = [ 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", 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" ]; ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- 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]]]; }; CheckpointButton: Buttons.ButtonProc = { okMsg: ROPE = IO.PutFR["Checkpoint created at %t.", IO.time[BasicTime.Now[]]]; msg: ROPE; PostMessage[NIL, "Creating checkpoint."]; msg _ Booting.Checkpoint[]; PostMessage[NIL, IF msg.Length[] = 0 THEN okMsg ELSE msg]; }; 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]]], []]]; }; BootButtonProc: Buttons.ButtonProc = { IF ~SomethingWorthSaving[] THEN PostMessage[NIL, Booting.Boot[[bootButton[]], []]]; }; BasicCedarDorado: Buttons.ButtonProc = { file: File.Handle = DirLookup["BasicCedarDorado.boot"]; IF file ~= NIL THEN { switches: Booting.Switches; error: ROPE; [switches, error] _ RopeToSwitches[UserProfile.Line["FileSwitches"]]; IF error = NIL AND ~SomethingWorthSaving[] THEN error _ Booting.Boot[[file[file]], switches]; PostMessage[NIL, error]; } ELSE PostMessage[NIL, "BasicCedarDorado.boot 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] = { fsFile: FS.OpenFile _ [NIL]; fsFile _ FS.Open[fileName ! FS.Error => CONTINUE]; IF fsFile ~= NIL THEN file _ FSBackdoor.GetFileHandle[fsFile]; }; RopeToSwitches: PROC [textSwitches: ROPE] 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]]; }; [] _ 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] }; ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- Initialize: ENTRY PROC = { ENABLE UNWIND => NULL; IF initialized THEN RETURN; IF ~File.IsDebugger[File.SystemVolume[]] THEN { [] _ q.CreateButton[ info: [name: "Checkpoint"], proc: CheckpointButton, fork: TRUE, guarded: TRUE]; [] _ q.CreateButton[ info: [name: "RollBack"], proc: RollbackButton, fork: TRUE, guarded: TRUE]; }; [] _ Buttons.Create[info: [name: "Boot"], proc: MakeBootTool, fork: TRUE]; initialized _ TRUE; }; Initialize[]; END. ŒBootTool.mesa last edited by Roy Levin on October 5, 1983 12:20 pm last edited by Andrew Birrell on February 6, 1983 11:41 am Last Edited by: Maxwell, February 23, 1983 9:30 am Last Edited by: Paul Rovner, December 13, 1983 10:40 am Global Variables (monitored) Tool Construction 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. [] _ q.CreateButton[info: [name: "Self"], proc: SelfBootProc]; MakeBootee[self, "Self", NIL, NIL]; The following turns off pending delete, and sets the type-in point at the end of the text if user editing is not inhibited Button Action Procedures Kill pending delete selection elsewhere in tool Kill pending delete selection elsewhere in tool Action Procedures SelfBootProc: Buttons.ButtonProc = { IF ~SomethingWorthSaving[] THEN PostMessage[NIL, Booting.Boot[[self[]], Booting.switches]]; }; This procedure can't be INLINE until Trinity (catch phrase in caller). self => { PostMessage[handle, Booting.Boot[[self[]], ProcessOptions[]]]; }; The Main Body Κx– "Cedar" style˜Jšœ ™ Jšœ4™4Jšœ:™:J™2J™7J™šΟk ˜ Jšœ œ˜JšœœΟc œ+˜DJšœœœ ˜.Jšœ œ˜šœœ˜Jšœ]˜]—Jšœœ˜+Jšœ œ/˜?Jšœœ˜$šœœ˜ Jšœ[˜[—Jšœœ6˜>Jšœ œ˜!Jšœ œ ˜Jšœœ˜Jšœœ ˜Jšœœ&˜0Jšœœ2˜?Jšœœ˜-Jšœœ4˜HJšœœ ˜Jšœœ4œ ˜LJšœœ˜Jšœ œ˜Jšœœ˜'Jšœœ ˜šœ œ˜JšœN˜N—Jšœ œ˜#šœ œ˜J˜k—J˜—Jšœ œ˜J˜š˜JšœRœœˆ˜μJ˜—Jš˜J˜Jšœœœ˜J˜Jšœ œ˜+Jšœ œ˜+Jšœ œ˜Jšœœ˜J˜Jšœœœ˜-J˜šœœœ˜"Jšœœ˜"Jšœœ˜Jšœœ˜Jš œ œ œ œœ˜0Jšœ!œ˜%Jšœ!œ˜%Jšœ!˜$J˜—J˜Jšœœœ˜,J˜šœœœ˜ Jšœ˜Jšœ ˜J˜—Jšžœžœžœžœžœžœžœž˜'J™Jšžœžœžœžœžœžœžœž˜'J˜Jšœ$˜$J˜Jšœ œœ˜J˜Jšœœœž&˜FJ˜Jšžœžœžœžœžœžœžœž˜'Jšœ™Jšžœžœžœžœžœžœžœž˜'J˜J˜šœœ˜*Jšœœœ˜šœ˜Jšœœ˜5J˜Jšœ@˜@Jšœ˜—Jšœ4œ˜=J˜—J˜šΟnœœœœ˜&Jšœœ˜#˜(Jšœœœ˜CJšœœž˜*Jšœ˜—JšœΫ™ΫJšœ9˜9J˜J˜J˜Jšœ5˜5Jšœ/œ˜6J˜J˜—šŸœœ œœ˜.Jšœœ˜Jšœœœ˜šœ&œ˜1Jšœœ˜(Jšœ œ˜#Jšœœ ˜=šœ˜Jšœ)˜)Jšœ œ&˜5Jšœ˜—Jšœ˜—JšœC˜CJšœ>˜>Jšœ>˜>Jšœ>™>Jšœœ˜Jšœ˜J˜—š Ÿ œœœœ œ˜;Jšœ˜Jšœ"˜"Jšœœ9˜VJ˜J˜—šŸœœ˜4˜'J˜J˜J˜J˜Jšœ˜—Jšœ,ž'˜SJšœ-ž˜BJ˜J˜—šŸœœ œ˜@˜J˜ J˜J˜>J˜J˜Jšœ˜ Jšœ˜—J˜:J˜J˜—Jšžœžœžœžœžœžœžœž˜'J˜Jšœ œž œ˜J˜J˜6J˜Jšœ ž.˜9J˜Jšœœ˜ Jšœ œ˜Jšœ˜ Jšœ˜—J˜OJšœ œ6˜FJšœ<˜Jšœ˜—J˜šŸœœ˜:šœ˜Jšœ˜JšœœQ˜ZJ˜—J˜J˜—šŸœœ˜=Jšœœ˜)š Ÿ œœœœœ˜,JšœœA˜Hšœ˜šœ˜!Jš œ œœœœ˜FJšœ˜——J˜—šŸœœœ˜,šœœ ˜Jšœ œœœ˜@Jšœœ˜ J˜—Jšœ5œ œ˜MJ˜—Jšœœ˜Jšœ œœ˜/š˜šœ8œœ˜KJšœ&œ!˜MJšœ˜——Jšœ˜Jšœ˜J˜—šŸœœœ˜JJšœœ$˜?šœœ˜ Jšœœ2˜TJšœ˜—J˜—J˜šŸœœ˜;šœ˜Jšœ˜šœœ˜Jšœ˜Jšœ'˜'Jšœ˜—J˜—J˜—J˜šŸœœ˜>Jšœ˜#Jšœ ˜Jšœ œœœ(˜[Jš˜Jšœžœœœ˜)š Ÿœœœœ œ˜6Jšœœ˜Jšœ œœ˜Jšœ4˜4š˜Jšœœ ˜šœœ"œ ˜OJ˜Jšœ˜—J˜;Jšœœœœ˜4Jšœ œ.œ˜EJ˜Jšœ˜—J˜—Jšœœœ˜3šœœœ˜6Jšœœ'˜1Jšœ˜Jšœ œ/˜Kšœ œ˜Jšœœ6˜QJ˜ —Jšœ˜—Jšœ˜Jšœ˜Jšœ˜J˜—šŸœœ˜6Jšœ œ˜/Jšœœ˜Jšœ œ œ˜šœ œœ˜5Jšœœ˜ Jšœœ ˜J˜Jšœ+œ˜Ešœ ˜šœœ˜Jšœœ$˜?Jšœ˜Jšœ˜——Jšœ˜—JšœA˜AJ˜—J˜Jšžœžœžœžœžœžœžœž˜'J˜šŸ œœ˜0Jšœ˜J˜J˜-šœ˜˜J˜J˜Jšœž4˜GJ˜Jšœ˜ J˜—J˜J˜J˜—˜'˜Jšœ˜J˜Jšœž4˜GJ˜Jšœ˜ J˜—J˜Jšœœ˜ J˜J˜—˜7J˜J˜FJ˜Jšœ ž=˜FJ˜Jšœœž˜Jšœ œ˜Jšœ˜ Jšœ˜—J˜HJšœL˜LJ˜:J˜J˜—Jšžœžœžœžœžœžœžœž˜'J˜šŸ œœ˜0Jšœœ ˜3J˜˜7J˜J˜J˜Jšœ ž=˜FJšœ˜Jšœœž˜Jšœ œ˜Jšœ˜ Jšœ˜—J˜HJ˜HJšœ.˜.Jšœ=˜=J˜J˜—Jšžœžœžœžœžœžœžœž˜'Jšœ™Jšžœžœžœžœžœžœžœž˜'J˜šœ*˜*Jšœœ ˜,JšœœC˜YJšœ/™/˜@JšœM˜P—Jšœ$˜$J˜J˜—šœ(˜(Jšœœ ˜,šœœ˜JšœG˜G—JšœRœ˜Wšœœ˜šœ œœ˜BJ˜F—Jšœ"˜"J˜—J˜J˜—šœ-˜-Jšœœ ˜,šœœ˜JšœH˜H—JšœSœ˜XJšœœ$˜>J˜J˜—šœ2˜2Jšœœ ˜,Jšœœ?˜UJšœ/™/šœ<˜Jšœœœ˜'Jšœœ˜&šœœ˜Jšœœ˜ Jšœ1˜1Jšœœ3œ˜UJ˜—šœœ˜-šœ˜šœ˜$Jšœ9˜9—šœœ˜JšœG˜G——Jšœ˜—Jšœ,˜,J˜—J˜Jšœœœœ˜"šœ œœ˜8J˜1J˜6J˜-J˜1J˜[J˜PJ˜#J˜#J˜'J˜RJ˜GJ˜CJ˜UJ˜2J˜GJ˜'J˜:J˜4J˜™>J™—Jšœ˜—J˜—Jšœœœ˜&Jšœœ˜%J˜J˜—š Ÿ œœ œœœ˜FJšœœ œ˜Jšœ œœ œ˜2Jšœ œœ)˜>J˜—J˜šŸœœœ˜)Jšœ*œœ˜@Jšœœœ˜š Ÿ œœœœ œ˜>šœœ˜Jšœœœœ˜SJšœœœ œ˜HJšœ-˜4—J˜—Jšœ8˜9JšœœA˜\J˜—J˜šŸœœœœ˜-Jšœœœ˜šœ"˜"Jš œœ œ œœœ˜;Jšœœ!˜5Jšœ˜—J˜'šœœ˜J˜šœ;œ˜CJ˜&Jšœœ˜ Jšœ˜—Jšœ˜—Jšœœ˜ Jšœ˜J˜J˜J˜—Jšžœžœžœžœžœžœžœž˜'Jšœ ™ Jšžœžœžœžœžœžœžœž˜'J˜šŸ œœœ˜Jšœœœ˜Jšœ œœ˜šœ'œ˜/šœ˜Jšœ:œ œ˜O—šœ˜Jšœ6œ œ˜K—J˜—JšœDœ˜JJšœœ˜Jšœ˜J˜—Jšœ ˜ J˜J™J˜Jšœ˜J˜J˜—…—^&~*