BootTool.mesa
Copyright © 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
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
];
Global Variables (monitored)
q: MBQueue.Queue ← MBQueue.Create[];
initialized: BOOLFALSE;
simpleButtonsUp: BOOLFALSE; -- protected by synchronization on 'q'
MakeBootTool: ENTRY Buttons.ButtonProc = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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
];
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.
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: BOOLFALSE] = {
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: BOOLFALSE] = {
name: ROPE =
IF bootee.currentChoice = NIL THEN NIL
ELSE NARROW[bootee.currentChoice.first, ChoiceRef].name;
ViewerTools.SetContents[bootee.displayedChoice, name];
IF deselect THEN
The following turns off pending delete, and sets the type-in point at the end of the
text if user editing is not inhibited
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
handle: BootToolHandle = NARROW[clientData];
previousContents: ROPE = ViewerTools.GetContents[handle.bootees[volume].displayedChoice];
Kill pending delete selection elsewhere in tool
ViewerTools.SetSelection[handle.bootees[volume].displayedChoice,
NEW[ViewerTools.SelPosRec ← [start: Rope.Length[previousContents], length: 0]]];
AdvanceName[handle.bootees[volume]];
};
SelectFileBootee: Buttons.ButtonProc = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
handle: BootToolHandle = NARROW[clientData];
previousContents: ROPE = ViewerTools.GetContents[handle.bootees[pv].displayedChoice];
Kill pending delete selection elsewhere in tool
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 ~ ROPENIL;
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
PostMessage[NIL, Booting.Boot[[self[]], [r: TRUE]]];
};
SimpleBootProc: Buttons.ButtonProc = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
IF ~SomethingWorthSaving[] THEN
PostMessage[NIL, Booting.Boot[[microcode[Booting.GetBootFileNumber[altoMesa]]], []]];
};
BasicCedar: Buttons.ButtonProc = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
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 = {
[parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE]
CantBoot: ERROR = CODE;
DoBootInner: PROC = --INLINE-- {
This procedure can't be INLINE until Trinity (catch phrase in caller).
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: ROPENIL] = {
errors: ROPENIL;
SetBootSwitch: PROC [c: CHAR] RETURNS [stop: BOOLFALSE] = {
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: BOOLFALSE;
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: ROPENIL] = {
file: File.Handle = DirLookup[name];
IF file # NIL THEN {
There is a boot file specified, so boot it
switches ← RopeToSwitches[UserProfile.Line["BootTool.FileSwitches", "f"], switches].switches;
error ← Booting.Boot[ [file[file]], switches ];
};
};
BootCommand: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
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 {
This is a switch, so convert the character switches to boot switches
[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;
If the argument is a file name, then try to boot it.
ENDCASE => {
This may be a volume name, so try to boot it.
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 = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
MakeBootTool[NIL, NIL, blue, FALSE, FALSE];
};
bootFileName: ROPESELECT 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.