<> <> <> <> <> <<>> 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]; <<[] _ q.CreateButton[info: [name: "Self"], proc: SelfBootProc];>> 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; }; ---- ---- ---- ---- ---- ---- ---- ---- <