<> <> <> <> <<>> DIRECTORY AMBridge USING [TVToCardinal], AMEventsExtra USING [Boot], AMTypes USING [--Error,-- Index, IndexToName, NValues, Type, Value], Ascii USING [SP], Booting USING [ Boot, Bootee, BootFileNumber, defaultSwitches, GetBootFileNumber, MakeBootable, MicrocodeType, nullMicrocode, Switches], Buttons USING [Button, ButtonProc, Create, SetDisplayStyle], Containers USING [ChildXBound, Container, Create], ConvertUnsafe USING [AppendRope, ToRefText, ToRope], DCSFileTypes USING [tLeaderPage], Directory USING [Error, GetNext, ignore, Lookup, VolumeError], File USING [Capability, GetAttributes, nullCapability, PageNumber], Heap USING [systemZone], IO USING [char, EndOf, Error, GetCard, PutFToRope, rope, RIS, STREAM], Labels USING [Create], List USING [Compare, Comparison, Reverse, Sort], Menus USING [AppendMenuEntry, CreateMenu, CreateEntry, Menu, MenuLine, MenuProc], MessageWindow USING [Append, Blink, Clear, Confirm], PhysicalVolume USING [ GetAttributes, GetNext, GetNextLogicalVolume, ID, maxNameLength, nullID], Process USING [Detach], Rope USING [ Cat, Concat, Equal, Fetch, Find, FromChar, FromRefText, Length, Lower, Map, ROPE, Substr, ToRefText, Upper], Rules USING [Create, Rule], Runtime USING[IsBound], UserProfile USING [Line], VFonts USING [FontHeight, StringWidth], ViewerClasses USING [Viewer], ViewerOps USING [ChangeColumn, EnumerateViewers, EnumProc, OpenIcon, SetMenu, SetOpenHeight], ViewerSpecs USING [openRightWidth], ViewerTools USING [ GetContents, InhibitUserEdits, MakeNewTextViewer, SelPosRec, SetContents, SetSelection], Volume USING [ GetLabelString, GetNext, ID, maxNameLength, NeedsScavenging, nullID, systemID, Unknown], VolumeExtras USING [OpenVolume]; BootTool: CEDAR MONITOR IMPORTS AMBridge, AMEventsExtra, AMTypes, Booting, Buttons, Containers, ConvertUnsafe, Directory, File, Heap, IO, Labels, List, Menus, MessageWindow, PhysicalVolume, Process, Rope, Rules, Runtime, UserProfile, VFonts, ViewerOps, ViewerSpecs, ViewerTools, Volume, VolumeExtras = BEGIN entryHeight: NAT = VFonts.FontHeight[] + 2; entryVSpace: NAT = VFonts.FontHeight[]*2/3; entryHSpace: NAT = 10; BootToolHandle: TYPE = REF BootToolHandleRec; BootToolHandleRec: TYPE = RECORD [ outer: Containers.Container _ NIL, height: NAT _ 0, s1Data: S1Data, s2Data: S2Data ]; SimpleButtonRef: TYPE = REF SimpleButtonRec; SimpleButtonRec: TYPE = RECORD [ volume: Volume.ID, switches: Rope.ROPE ]; ---- ---- ---- ---- ---- ---- ---- ---- <> ---- ---- ---- ---- ---- ---- ---- ---- MakeBootTool: ENTRY Buttons.ButtonProc = { menu: Menus.Menu; handle: BootToolHandle; IF mouseButton # blue THEN {MakeBootButtons[]; RETURN}; menu _ Menus.CreateMenu[lines: 2]; handle _ NEW[BootToolHandleRec]; handle.outer _ Containers.Create[info: [ name: "Boot Tool", iconic: TRUE, column: right, scrollable: FALSE], paint: FALSE]; MakeSimpleButtonsMenu[menu, 0]; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: "Boot", proc: DoBoot, clientData: handle, fork: TRUE, guarded: TRUE], 1]; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: "Reset", proc: ResetDefaults, clientData: handle, fork: TRUE], 1]; ViewerOps.SetMenu[viewer: handle.outer, menu: menu]; BuildSection1[handle]; BuildSection2[handle]; ResetDefaultsInternal[handle]; ViewerOps.SetOpenHeight[handle.outer, handle.height]; ViewerOps.OpenIcon[icon: handle.outer, bottom: FALSE]; }; buttonsCreated: BOOLEAN _ FALSE; MakeBootButtons: PROC = { volID: Volume.ID _ Volume.nullID; IF buttonsCreated THEN RETURN ELSE buttonsCreated _ TRUE; UNTIL (volID _ Volume.GetNext[volID, ALL[TRUE]]) = Volume.nullID DO volName: STRING _ [Volume.maxNameLength]; label, switches: Rope.ROPE; TRUSTED{ Volume.GetLabelString[volID, volName]; label_ConvertUnsafe.ToRope[volName] }; IF (switches _ GetSwitches[label]).Length[] > 0 THEN label _ label.Cat["/", switches]; [] _ Buttons.Create[info: [name: label], proc: SimpleBootProc, clientData: NEW[SimpleButtonRec _ [volID, switches]], fork: TRUE, guarded: TRUE]; ENDLOOP; [] _ Buttons.Create[info: [name: "Alto"], proc: AltoBootProc, clientData: NIL, fork: TRUE, guarded: TRUE]; [] _ Buttons.Create[info: [name: "BB"], proc: BootButtonProc, clientData: NIL, fork: TRUE, guarded: TRUE]; [] _ Buttons.Create[info: [name: "Basic"], proc: BasicCedarDorado, clientData: NIL, fork: TRUE, guarded: TRUE]; }; MakeSimpleButtonsMenu: INTERNAL PROC [menu: Menus.Menu, line: Menus.MenuLine] = { volID: Volume.ID _ Volume.nullID; UNTIL (volID _ Volume.GetNext[volID, ALL[TRUE]]) = Volume.nullID DO volName: STRING _ [Volume.maxNameLength]; label, switches: Rope.ROPE; TRUSTED{ Volume.GetLabelString[volID, volName]; label_ConvertUnsafe.ToRope[volName] }; IF (switches _ GetSwitches[label]).Length[] > 0 THEN label _ label.Cat["/", switches]; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: label, proc: SimpleBootProc, clientData: NEW[SimpleButtonRec _ [volID, switches]], fork: TRUE, guarded: TRUE], line]; ENDLOOP; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: "Alto", proc: AltoBootProc, clientData: NIL, fork: TRUE, guarded: TRUE], line]; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: "BB", proc: BootButtonProc, clientData: NIL, fork: TRUE, guarded: TRUE], line]; Menus.AppendMenuEntry[menu, Menus.CreateEntry[name: "Basic", proc: BasicCedarDorado, clientData: NIL, fork: TRUE, guarded: TRUE], line]; }; GetSwitches: PROC [name: Rope.ROPE] RETURNS [switches: Rope.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.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; }; ResetDefaultsInternal: INTERNAL PROC [handle: BootToolHandle] = { ResetSection1[handle]; ResetSection2[handle]; }; ---- ---- ---- ---- ---- ---- ---- ---- Section1Data: TYPE = RECORD [ selected: BooteeClass _ volume, bootees: ARRAY BooteeClass OF Bootee _ ALL[NIL], microcodeListDone: BOOL _ FALSE, microcodeListBuilt: CONDITION _ [timeout: 0] ]; S1Data: TYPE = REF Section1Data; BooteeClass: TYPE = {volume, file, ucode, physicalVolume}; Bootee: TYPE = REF BooteeObject; BooteeObject: TYPE = RECORD [ 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.ROPE, value: REF ANY]; VolumeVal: TYPE = REF Volume.ID; FileVal: TYPE = REF File.Capability; MicrocodeVal: TYPE = REF Booting.BootFileNumber; PhysicalVolumeVal: TYPE = REF PhysicalVolume.ID; BuildSection1: INTERNAL PROC [handle: BootToolHandle] = { s1Data: S1Data = NEW[Section1Data _ []]; MakeBootee: PROC [ buttonName: Rope.ROPE, buttonProc: Buttons.ButtonProc, choicesProc: PROC RETURNS [ChoiceList] _ NIL] RETURNS [bootee: Bootee] = { bootee _ NEW[BooteeObject _ []]; bootee.button _ Buttons.Create[ info: [name: buttonName, wx: entryHSpace, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE ], proc: buttonProc, fork: TRUE, clientData: handle ]; IF choicesProc ~= NIL THEN bootee.choices _ choicesProc[]; bootee.displayedChoice _ ViewerTools.MakeNewTextViewer[info: [ parent: handle.outer, wx: bootee.button.wx + bootee.button.ww + entryHSpace, wy: handle.height, ww: 50, -- ugly, but necessary preparation for Containers.ChildXBound wh: entryHeight, data: NIL, scrollable: FALSE, border: FALSE ]]; Containers.ChildXBound[container: handle.outer, child: bootee.displayedChoice]; handle.height _ handle.height + entryHeight + entryVSpace; }; AddSeparatingRule[handle]; MakeCenteredLabel[handle, "Booting Sources"]; s1Data.bootees[volume] _ MakeBootee["Volume", SelectVolumeBootee, BuildVolumeList]; ViewerTools.InhibitUserEdits[s1Data.bootees[volume].displayedChoice]; s1Data.bootees[file] _ MakeBootee["File", SelectFileBootee]; TRUSTED{ Process.Detach[FORK BuildBootFileListEntry[handle]] }; s1Data.bootees[ucode] _ MakeBootee["Microcode", SelectMicrocodeBootee]; s1Data.bootees[physicalVolume] _ MakeBootee["Physical Volume", SelectPhysicalVolumeBootee, BuildPhysicalVolumeList]; ViewerTools.InhibitUserEdits[s1Data.bootees[physicalVolume].displayedChoice]; handle.s1Data _ s1Data; BuildMicrocodeList[handle]; }; ResetSection1: INTERNAL PROC [handle: BootToolHandle] = { s1Data: S1Data = handle.s1Data; SelectBootee[s1Data, volume]; s1Data.bootees[volume].currentChoice _ s1Data.bootees[volume].choices; SetChoice[s1Data.bootees[volume]]; s1Data.bootees[file].choices _ s1Data.bootees[file].currentChoice _ NIL; SetChoice[s1Data.bootees[file]]; s1Data.bootees[ucode].currentChoice _ s1Data.bootees[ucode].choices; SetChoice[s1Data.bootees[ucode]]; s1Data.bootees[physicalVolume].currentChoice _ s1Data.bootees[physicalVolume].choices; SetChoice[s1Data.bootees[physicalVolume]]; }; SetChoice: PROC [bootee: Bootee, deselect: BOOL _ FALSE] = { name: Rope.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: INTERNAL PROC RETURNS [volumes: ChoiceList _ NIL] = { volume: Volume.ID _ Volume.nullID; UNTIL (volume _ Volume.GetNext[volume, ALL[TRUE]]) = Volume.nullID DO IF volume ~= Volume.systemID THEN volumes _ CONS[VolumeToChoice[volume], volumes]; ENDLOOP; volumes _ List.Sort[volumes, CompareChoiceNames]; RETURN [CONS[SystemVolumeChoice[], volumes]] }; SystemVolumeChoice: PROC RETURNS [ChoiceRef] = INLINE {RETURN [VolumeToChoice[Volume.systemID]]}; VolumeToChoice: PROC [volume: Volume.ID] RETURNS [ChoiceRef] = TRUSTED { volName: STRING = [Volume.maxNameLength]; volID: VolumeVal = NEW[Volume.ID _ volume]; Volume.GetLabelString[volume, volName]; RETURN [NEW[ChoiceDesc _ [Rope.FromRefText[ConvertUnsafe.ToRefText[volName]], volID]]] }; CompareChoiceNames: PROC [ref1, ref2: REF ANY] RETURNS [List.Comparison] = {RETURN[List.Compare[NARROW[ref1, ChoiceRef].name, NARROW[ref2, ChoiceRef].name]]}; BuildBootFileListEntry: ENTRY PROC [handle: BootToolHandle] = { <> handle.s1Data.bootees[file].choices _ BuildBootFileList[handle ! ANY => CONTINUE]}; BuildBootFileList: INTERNAL PROC [handle: BootToolHandle] RETURNS [bootFiles: ChoiceList _ NIL] = { NormalizeFileName: PROC [file: Rope.ROPE] RETURNS [front, back: Rope.ROPE _ NIL] = { <<back, SysDir>back, volume>back, volume>SysDir>back all yield front = SysDir>>> <back and back yield front = NIL>> sysDir: Rope.ROPE = "SysDir>"; i: INT _ file.Find[sysDir, 0, FALSE]; IF i >= 0 THEN file _ Rope.Concat[file.Substr[0, i], file.Substr[i+sysDir.Length[]]]; IF (i _ file.Find[">"]) = -1 THEN {back _ file; RETURN}; IF file.Fetch[0] ~= '< THEN front _ Rope.Concat["<", file.Substr[0, i+1]] ELSE front _ file.Substr[0, i+1]; front _ front.Concat[sysDir]; back _ file.Substr[i+1]; }; GetNextBootFile: PROC [file: Rope.ROPE] RETURNS [nextFile: Rope.ROPE, nextCap: File.Capability] = TRUSTED { path, thisFile: Rope.ROPE; pathS: LONG STRING; EndsInDotBoot: PROC [file: LONG STRING] RETURNS [BOOL] = TRUSTED { bootExt: STRING = ".boot"L; bootLen: NAT = bootExt.length; len: NAT = IF file = NIL THEN 0 ELSE file.length; IF len < bootLen THEN RETURN[FALSE]; FOR i: NAT IN [0..bootLen) DO IF Rope.Lower[file[len-bootLen+i]] ~= bootExt[i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] }; <> <> thisFileS: LONG STRING = [100]; [path, thisFile] _ NormalizeFileName[file]; pathS _ LOOPHOLE[Rope.ToRefText[path]]; ConvertUnsafe.AppendRope[thisFileS, thisFile]; DO nextFile _ NIL; nextCap _ Directory.GetNext[ pathName: pathS, currentName: thisFileS, nextName: thisFileS ! Directory.VolumeError--[volume]-- => IF type = volumeNotOpen THEN { VolumeExtras.OpenVolume[volume: volume, readOnly: TRUE ! Volume.Unknown, Volume.NeedsScavenging => EXIT]; RETRY} ELSE EXIT ]; SELECT TRUE FROM thisFileS.length = 0 => EXIT; EndsInDotBoot[thisFileS] => {nextFile _ ConvertUnsafe.ToRope[thisFileS]; EXIT}; ENDCASE; ENDLOOP; }; DoBootFilesWithPrefix: PROC [file: Rope.ROPE] = { DO cap: File.Capability; <> [file, cap] _ GetNextBootFile[file ! Directory.Error, Directory.VolumeError => { PostMessage[handle, "Incorrect file path"]; EXIT}]; IF file = NIL THEN EXIT; bootFiles _ CONS[NEW[ChoiceDesc _ [file, NEW[File.Capability _ cap]]], bootFiles]; ENDLOOP; }; front, back: Rope.ROPE; [front, back] _ NormalizeFileName[ViewerTools.GetContents[handle.s1Data.bootees[file].displayedChoice]]; IF front.Length[] > 0 AND back.Length[] = 0 THEN DoBootFilesWithPrefix[front] ELSE FOR c: ChoiceList _ handle.s1Data.bootees[volume].choices, c.rest UNTIL c = NIL DO DoBootFilesWithPrefix[Rope.Cat["<", NARROW[c.first, ChoiceRef].name, ">SysDir>"]]; ENDLOOP; RETURN [List.Reverse[bootFiles]] }; BuildMicrocodeList: INTERNAL PROC [handle: BootToolHandle] = { OPEN Booting; handle.s1Data.bootees[ucode].choices _ LIST[NEW[ChoiceDesc _ ["(default)", NEW[BootFileNumber _ nullMicrocode]]]]; TRUSTED{ Process.Detach[FORK BuildFullMicrocodeList[handle]] }; }; BuildFullMicrocodeList: PROC [handle: BootToolHandle] = { OPEN Booting; InstallMicrocodeList: ENTRY PROC [handle: BootToolHandle, microcode: ChoiceList] = { handle.s1Data.bootees[ucode].choices _ microcode; handle.s1Data.microcodeListDone _ TRUE; NOTIFY handle.s1Data.microcodeListBuilt; }; microcode: ChoiceList _ LIST[NEW[ChoiceDesc _ ["(default)", NEW[BootFileNumber _ nullMicrocode]]]]; BEGIN ENABLE --AMTypes.Error-- ANY => CONTINUE; Massage: PROC [name: Rope.ROPE] RETURNS [newName: Rope.ROPE] = { OPEN Rope; XOR: PROC [a, b: BOOL] RETURNS [BOOL] = INLINE {RETURN [(a AND b) OR (~a AND ~b)]}; start: INT _ 1; doingLower: BOOL _ FALSE; newName _ FromChar[Upper[Fetch[name, 0]]]; DO next: INT _ start; UNTIL next = Length[name] OR XOR[doingLower, ~(Fetch[name, next] IN ['a..'z])] DO next _ next + 1; ENDLOOP; newName _ Concat[newName, Substr[name, start, next - start]]; IF next = Length[name] THEN EXIT ELSE start _ next; IF doingLower THEN newName _ Concat[newName, FromChar[Ascii.SP]]; doingLower _ ~doingLower; ENDLOOP; }; uT: AMTypes.Type = LOOPHOLE[CODE[MicrocodeType]]; FOR i: AMTypes.Index IN [1..AMTypes.NValues[uT]] DO name: Rope.ROPE = Massage[AMTypes.IndexToName[uT, i]]; ucodeType: MicrocodeType; TRUSTED{ ucodeType _ LOOPHOLE[AMBridge.TVToCardinal[AMTypes.Value[uT, i]]] }; microcode _ CONS[ NEW[ChoiceDesc _ [name, NEW[BootFileNumber _ GetBootFileNumber[ucodeType]]]], microcode]; ENDLOOP; END; InstallMicrocodeList[handle, List.Reverse[microcode]]; }; BuildPhysicalVolumeList: INTERNAL PROC RETURNS [physicalVolumes: ChoiceList _ NIL] = TRUSTED { physicalVolume: PhysicalVolume.ID _ PhysicalVolume.nullID; UNTIL (physicalVolume _ PhysicalVolume.GetNext[physicalVolume]) = PhysicalVolume.nullID DO IF PhysicalVolume.GetNextLogicalVolume[physicalVolume, Volume.nullID] ~= Volume.nullID THEN physicalVolumes _ CONS[PhysicalVolumeToChoice[physicalVolume], physicalVolumes]; ENDLOOP; physicalVolumes _ List.Sort[physicalVolumes, CompareChoiceNames]; }; PhysicalVolumeToChoice: PROC [physicalVolume: PhysicalVolume.ID] RETURNS [ChoiceRef] = TRUSTED { volName: STRING = [PhysicalVolume.maxNameLength]; volID: PhysicalVolumeVal = NEW[PhysicalVolume.ID _ physicalVolume]; [] _ PhysicalVolume.GetAttributes[physicalVolume, volName]; RETURN [NEW[ChoiceDesc _ [Rope.FromRefText[ConvertUnsafe.ToRefText[volName]], volID]]] }; ---- ---- ---- ---- ---- ---- ---- ---- Section2Data: TYPE = RECORD [ switchesButton: Buttons.Button _ NIL, switches: ViewerClasses.Viewer _ NIL ]; S2Data: TYPE = REF Section2Data; BuildSection2: INTERNAL PROC [handle: BootToolHandle] = { s2Data: S2Data = NEW[Section2Data _ []]; AddSeparatingRule[handle]; MakeCenteredLabel[handle, "Booting Options"]; s2Data.switchesButton _ Buttons.Create[ info: [name: "Switches:", wx: entryHSpace, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: FALSE ], proc: SelectBootSwitches, fork: TRUE, clientData: handle ]; s2Data.switches _ ViewerTools.MakeNewTextViewer[info: [ parent: handle.outer, wx: s2Data.switchesButton.wx + s2Data.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: s2Data.switches]; handle.height _ handle.height + entryHeight + entryVSpace; handle.s2Data _ s2Data; }; ResetSection2: INTERNAL PROC [handle: BootToolHandle] = { s2Data: S2Data = handle.s2Data; ViewerTools.SetContents[s2Data.switches, UserProfile.Line["Boot.Switches"]]; }; ---- ---- ---- ---- ---- ---- ---- ---- <