DIRECTORY Atom USING[ GetPName, MakeAtom ], Buttons USING[ Button, ButtonProc, Create, Destroy, SetDisplayStyle ], Commander USING[ CommandProc, Register ], Containers USING[ ChildXBound, ChildYBound, Create ], Disk, DiskFace USING[ Op ], IO USING[ EndOfStream, GetChar, GetInt, PutChar, PutF, PutFR, RIS, RopeFromROS, ROS, STREAM, Error ], Labels USING[ Create, Set ], MBQueue USING[ Create, CreateButton, Queue, QueueClientAction ], Menus USING[ MouseButton ], Rope USING[ Cat, ROPE ], Rules USING[ Create ], VolumeFormat USING[ Attributes ], ViewerClasses USING[ Viewer ], ViewerOps USING[ DestroyViewer, OpenIcon ], ViewerTools USING[ GetContents, InhibitUserEdits, MakeNewTextViewer, SetContents, SetSelection], VM USING[ Allocate, Interval, AddressForPageNumber, Pin, Unpin ]; DiskTool: CEDAR MONITOR IMPORTS Atom, Buttons, Commander, Containers, Disk, IO, Labels, MBQueue, Rope, Rules, ViewerOps, ViewerTools, VM EXPORTS DiskFace--Attributes-- = BEGIN DriveRec: TYPE = RECORD[channel: Disk.Channel, name: ATOM]; drives: LIST OF DriveRec _ NIL; GetDrives: ENTRY PROC RETURNS[l: LIST OF ATOM _ NIL] = TRUSTED BEGIN IF drives = NIL THEN BEGIN FOR h: Disk.Channel _ Disk.NextChannel[NIL], Disk.NextChannel[h] UNTIL h = NIL DO drives _ CONS[ first: [ channel: h, name: Atom.MakeAtom[ IO.PutFR["RD%g", [integer[Disk.DriveAttributes[h].ordinal]]]] ], rest: drives]; ENDLOOP; END; FOR dr: LIST OF DriveRec _ drives, dr.rest UNTIL dr = NIL DO l _ CONS[first: dr.first.name, rest: l] ENDLOOP; END; LookupDrive: ENTRY PROC[name: ATOM] RETURNS[Disk.Channel] = BEGIN FOR dr: LIST OF DriveRec _ drives, dr.rest UNTIL dr = NIL DO IF dr.first.name = name THEN RETURN[dr.first.channel]; ENDLOOP; ERROR END; dataInterval: VM.Interval = VM.Allocate[1]; label: Disk.Label; VRR: Buttons.ButtonProc = TRUSTED { d: MyData = NARROW[clientData]; DoCommand[d, [header: verify, label: read, data: read], mouseButton ]; }; VVR: Buttons.ButtonProc = TRUSTED { d: MyData = NARROW[clientData]; DoCommand[d, [header: verify, label: verify, data: read], mouseButton ]; }; VVW: Buttons.ButtonProc = TRUSTED { d: MyData = NARROW[clientData]; DoCommand[d, [header: verify, label: verify, data: write], mouseButton ]; }; VWW: Buttons.ButtonProc = TRUSTED { d: MyData = NARROW[clientData]; DoCommand[d, [header: verify, label: write, data: write], mouseButton ]; }; BadPageNumber: ERROR = CODE; GetPageNumber: PROC[d: MyData] RETURNS[Disk.PageNumber] = TRUSTED BEGIN pageR: Rope.ROPE = ViewerTools.GetContents[d.pageT]; in: IO.STREAM = IO.RIS[pageR]; RETURN[[in.GetInt[! IO.EndOfStream, IO.Error => { Msg[d, "Invalid page number"]; ERROR BadPageNumber[] } ]]] END; --DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes; DoCommand: PROC[d: MyData, command: Disk.Command, mouseButton: Menus.MouseButton] = TRUSTED BEGIN data: LONG POINTER = VM.AddressForPageNumber[dataInterval.page]; delta: INT = IF command.header = write OR command.label = write OR command.data = write THEN 0 ELSE SELECT mouseButton FROM red => 0, yellow => +1, blue => -1, ENDCASE => ERROR; page: Disk.PageNumber _ [GetPageNumber[d ! BadPageNumber => GOTO bad] + delta]; msg: Rope.ROPE = IO.PutFR["Page %b: %g%g%g ... ", [integer[page]], [character[Op[command.header]]], [character[Op[command.label]]], [character[Op[command.data]]] ]; handle: Disk.Channel _ LookupDrive[d.drive^]; req: Disk.Request _ [ diskPage: page, data: data, -- first data page command: command, count: 1 ]; status: Disk.Status; countDone: Disk.PageCount; IF command.label # read AND NOT ParseArray[d, d.labelT, LOOPHOLE[LONG[@label]], SIZE[Disk.Label]] THEN GOTO bad; IF command.data # read AND NOT ParseArray[d, d.dataT, LOOPHOLE[data], 256] THEN GOTO bad; ViewerTools.SetContents[d.pageT, IO.PutFR["%g", [integer[page]]]]; Msg[d, msg]; TRUSTED BEGIN ENABLE UNWIND => VM.Unpin[dataInterval]; VM.Pin[dataInterval]; [status, countDone] _ Disk.DoIO[handle, @label, @req]; VM.Unpin[dataInterval]; END; Msg[d, Rope.Cat[msg, WITH s: status SELECT FROM changed => "bad changeCount", unchanged => SELECT s.status FROM inProgress => "inProgress", goodCompletion => "ok", notReady => "notReady", recalibrateError => "recalibrateError", seekTimeout => "seekTimeout", headerCRCError => "headerCRCError", labelCRCError => "labelCRCError", dataCRCError => "dataCRCError", headerNotFound => "headerNotFound", labelVerifyError => "labelVerifyError", dataVerifyError => "dataVerifyError", overrunError => "overrunError", writeFault => "writeFault", memoryError => "memoryError", memoryFault => "memoryFault", clientError => "clientError", operationReset => "operationReset", otherError => "otherError", ENDCASE => "\"unchanged\"?", ENDCASE => "bad status"] ]; SetArray[d.labelT, LOOPHOLE[LONG[@label]], SIZE[Disk.Label], FALSE]; BEGIN attr: Attributes = label.attributes; ViewerTools.SetContents[d.labelDetailsT, IO.PutFR["type = %g, page = %b", SELECT attr FROM physicalRoot => [rope["physical root"]], badPageList => [rope["bad page list"]], badPage => [rope["bad page"]], subVolumeMarker => [rope["marker page"]], logicalRoot => [rope["logical root"]], freePage => [rope["free page"]], header => [rope["header"]], data => [rope["data"]], ENDCASE => [integer[LOOPHOLE[attr, CARDINAL]]], [integer[label.filePage]] ]]; END; SetArray[d.dataT, LOOPHOLE[data], 256, TRUE]; EXITS bad => NULL; END; Op: PROC[op: DiskFace.Op] RETURNS[CHAR] = { RETURN[SELECT op FROM noOp => 'n, read => 'r, write => 'w, verify => 'v, ENDCASE => ERROR] }; SetArray: PROC[v: ViewerClasses.Viewer, p: LONG POINTER TO ARRAY OF CARDINAL, n: INT, label: BOOL] = TRUSTED BEGIN out: IO.STREAM = IO.ROS[]; FOR i: CARDINAL IN CARDINAL[0..n) DO IF (i MOD 8) = 0 AND label THEN {IF i # 0 THEN out.PutChar['\n]; out.PutF["%b:\t", [cardinal[i]] ] }; out.PutF["%b", [cardinal[p[i]]] ]; IF label THEN BEGIN out.PutChar['\t]; IF p[i] < 10000B THEN out.PutChar['\t]; IF p[i] < 10B THEN out.PutChar['\t]; END ELSE out.PutChar[' ]; ENDLOOP; ViewerTools.SetContents[v, IO.RopeFromROS[out] ]; END; ParseArray: PROC[d: MyData, v: ViewerClasses.Viewer, p: LONG POINTER TO ARRAY OF CARDINAL, n: INT] RETURNS[ok: BOOL _ TRUE] = TRUSTED BEGIN in: IO.STREAM = IO.RIS[ViewerTools.GetContents[v]]; pos: CARDINAL _ 0; DO temp: INT = in.GetInt[! IO.EndOfStream => { Msg[d, "Too few values in data/label text"]; GOTO bad}; IO.Error => { Msg[d, "Number expected in data/label text"]; GOTO bad} ]; c: CHAR = in.GetChar[]; SELECT c FROM ': => IF temp # pos THEN { Msg[d, "Incorrectly numbered position in data/label text"]; GOTO bad}; ',, ' , '\n, '\t => { p[pos] _ temp; pos _ pos+1; IF pos = n THEN EXIT }; ENDCASE => { Msg[d, "Unexpected character in data/label text"]; GOTO bad}; ENDLOOP; EXITS bad => RETURN[FALSE]; END; Msg: PROC[d: MyData, msg: Rope.ROPE] = BEGIN Labels.Set[d.msgLabel, msg]; END; -- ******** Viewer management ******** -- MyData: TYPE = REF MyDataObject; MyDataObject: TYPE = MONITORED RECORD[ mbQueue: MBQueue.Queue _ NIL, pageT, msgLabel, labelT, labelDetailsT, dataT: ViewerClasses.Viewer _ NIL, maxW: INTEGER _ 0, buttH: INTEGER _ 0, drive: REF ATOM]; Create: Commander.CommandProc = BEGIN d: MyData _ NEW[MyDataObject]; d.mbQueue _ MBQueue.Create[]; MBQueue.QueueClientAction[d.mbQueue, ReallyCreate, d]; END; ReallyCreate: PROC[clientData: REF ANY] = -- This is a separate procedure so that it is synchronized with d.mbQueue -- { d: MyData = NARROW[clientData]; v: ViewerClasses.Viewer = Containers.Create[ info: [name: "Disk Tool", column: left, scrollable: FALSE, iconic: TRUE]]; child: ViewerClasses.Viewer _ NIL; x: INTEGER _ 2; y: INTEGER _ 0; CommandButton: PROC[name: Rope.ROPE, proc: Buttons.ButtonProc, data: REF ANY, newline: BOOL, guarded: BOOL _ FALSE] = { child _ MBQueue.CreateButton[ q: d.mbQueue, info: [name: name, parent: v, border: TRUE, wy: y, wx: x, ww: d.maxW], proc: proc, clientData: data, fork: TRUE, paint: TRUE, guarded: guarded]; x _ IF newline THEN 1 ELSE child.wx + d.maxW - 1; y _ IF newline THEN child.wy + child.wh + 2 ELSE child.wy; }; LabelText: PROC[name, data: Rope.ROPE, prev: ViewerClasses.Viewer] RETURNS[ViewerClasses.Viewer] = { scrollWidth: INTEGER = 12; child _ ViewerTools.MakeNewTextViewer[ info: [parent: v, wh: d.buttH, ww: 999, scrollable: TRUE, data: IF prev = NIL THEN data ELSE ViewerTools.GetContents[prev], border: FALSE, wx: x + d.maxW + 2 - scrollWidth, wy: y], paint: TRUE ]; Containers.ChildXBound[v, child]; [] _ Buttons.Create[ info: [name: name, parent: v, wh: d.buttH, border: FALSE, wx: x+1, wy: y], proc: TextLabelProc, clientData: child, fork: FALSE, paint: TRUE]; x _ 1; y _ child.wy + child.wh + 2; RETURN[child] }; Label: PROC[name: Rope.ROPE, newline: BOOL] = { child _ Labels.Create[ info: [name: name, parent: v, border: FALSE, wy: y, wx: x+1], paint: TRUE ]; x _ IF newline THEN 1 ELSE child.wx + d.maxW - 1; y _ IF newline THEN child.wy + child.wh + 2 ELSE child.wy; }; Rule: PROC = { child _ Rules.Create[ info: [parent: v, border: FALSE, wy: y, wx: 0, ww: v.ww, wh: 1], paint: TRUE ]; Containers.ChildXBound[v, child]; x _ 1; y _ child.wy + child.wh + 2; }; { -- kludge to find max button size! -- temp: Buttons.Button = Buttons.Create[ info: [name: "Incr Page ", parent: v, border: FALSE, wx: 0, wy: 0], proc: NIL, clientData: d, fork: FALSE, paint: FALSE]; d.maxW _ temp.ww; d.buttH _ temp.wh; Buttons.Destroy[temp]; }; child _ d.msgLabel _ Labels.Create[ info: [name: "Left => do it. Middle => increment and do it. Right => decrement and do it", parent: v, border: FALSE, ww: v.cw, wy: y, wx: x+1], paint: TRUE ]; Containers.ChildXBound[v, child]; x _ 1; y _ child.wy + child.wh + 2; Rule[]; [child, d.drive] _ CreateSelector[q: d.mbQueue, name: "Drive:", values: GetDrives[], init: NEW[ATOM_$RD0], change: NIL, clientData: d, viewer: v, x: x, y: y, w: d.maxW]; x _ 1; y _ child.wy + child.wh - 1; Label["Command:", FALSE]; CommandButton["vrr", VRR, d, FALSE]; CommandButton["vvr", VVR, d, FALSE]; CommandButton["vvw", VVW, d, FALSE, TRUE]; CommandButton["vww", VWW, d, TRUE, TRUE]; d.pageT _ LabelText["Page:", NIL, NIL]; d.labelT _ LabelText["Label:", NIL, NIL]; d.labelDetailsT _ LabelText["", NIL, NIL]; ViewerTools.InhibitUserEdits[d.labelDetailsT]; d.dataT _ LabelText["Data:", NIL, NIL]; Containers.ChildXBound[v, d.dataT]; Containers.ChildYBound[v, d.dataT]; ViewerOps.OpenIcon[v]; }; TextLabelProc: Buttons.ButtonProc = { text: ViewerClasses.Viewer = NARROW[clientData]; SELECT mouseButton FROM red => ViewerTools.SetSelection[text, NIL]; blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] }; yellow => NULL; ENDCASE => ERROR; }; Selector: TYPE = REF SelectorRec; SelectorRec: TYPE = RECORD[ value: REF ATOM, change: SelectorNotifier, clientData: REF ANY, buttons: LIST OF Buttons.Button, values: LIST OF ATOM ]; SelectorNotifier: TYPE = PROC[parent: ViewerClasses.Viewer, clientData: REF ANY, value: ATOM] RETURNS[BOOL]; CreateSelector: PROC[q: MBQueue.Queue, name: Rope.ROPE, values: LIST OF ATOM, init: REF ATOM _ NIL, change: SelectorNotifier _ NIL, clientData: REF ANY _ NIL, viewer: ViewerClasses.Viewer, x, y: INTEGER, w: INTEGER _ 0] RETURNS[child: ViewerClasses.Viewer, value: REF ATOM] = { selector: Selector _ NEW[ SelectorRec _ [value: IF init # NIL THEN init ELSE NEW[ATOM_values.first], change: change, clientData: clientData, buttons: NIL, values: values ] ]; last: LIST OF Buttons.Button _ NIL; value _ selector.value; child _ Labels.Create[ info: [name: name, parent: viewer, border: FALSE, wx: x+1, wy: y, ww: w] ]; FOR a: LIST OF ATOM _ values, a.rest UNTIL a = NIL DO child _ MBQueue.CreateButton[ q: q, info: [name: Atom.GetPName[a.first], parent: viewer, border: TRUE, wy: child.wy, wx: child.wx + child.ww - 1, ww: w], proc: SelectorProc, clientData: selector, fork: TRUE, paint: TRUE]; IF last = NIL THEN last _ selector.buttons _ CONS[first: child, rest: NIL] ELSE { last.rest _ CONS[first: child, rest: NIL]; last _ last.rest }; IF a.first = selector.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack]; ENDLOOP; }; SelectorProc: Buttons.ButtonProc = { self: Buttons.Button = NARROW[parent]; selector: Selector = NARROW[clientData]; buttons: LIST OF Buttons.Button _ selector.buttons; FOR a: LIST OF ATOM _ selector.values, a.rest UNTIL a = NIL DO IF self = buttons.first THEN BEGIN IF selector.change = NIL OR selector.change[self.parent, selector.clientData, a.first] THEN BEGIN selector.value^ _ a.first; Buttons.SetDisplayStyle[self, $WhiteOnBlack]; FOR others: LIST OF Buttons.Button _ selector.buttons, others.rest UNTIL others = NIL DO IF others.first # self THEN Buttons.SetDisplayStyle[others.first, $BlackOnWhite] ENDLOOP; END; EXIT END; buttons _ buttons.rest; ENDLOOP; }; Commander.Register[key: "DiskTool", proc: Create, doc: "Reading and patching raw disk (wizards only!)"]; END. ÞCedar Nucleus: test program for investigating/patching raw disk pages. DiskTool.mesa Andrew Birrell, August 26, 1983 9:28 am parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL Thus, the buttons can't be invoked until we've finished creating them, and called SetWorld. parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL ÊX˜JšœF™FJšœ ™ Jšœ'™'J˜šÏk ˜ Jšœœ˜!Jšœœ9˜FJšœ œ˜)Jšœ œ%˜5J˜Jšœ œ˜Jš œœ6œœœ ˜eJšœœ˜Jšœœ3˜@Jšœœ˜Jšœœœ˜Jšœœ ˜Jšœ œ˜!Jšœœ ˜Jšœ œ˜+Jšœ œO˜`Jšœœ9˜A—J˜šœ œ˜Jšœ-œ8˜pJšœ Ïcœ˜ —J˜Jš˜J˜Jšœ œœœ˜;Jšœœœ œ˜J˜šÏn œœœœœœœœ˜>Jš˜Jšœ ˜šœ˜ šœ$œ˜,J˜—Jšœ˜ šœ œ˜˜J˜ ˜Jšœ;˜=—J˜—J˜—Jšœ˜Jšœ˜—Jš œœœœ˜9Jšœœ œ˜3Jšœ˜J˜—š Ÿ œœœœœ˜;Jš˜Jš œœœœ˜9Jš œœœœœ˜BJš˜Jšœ˜J˜—Jšœœ œ ˜+J˜J˜JšÐknœœ˜#šœT™TJšœ œ ˜JšœF˜FJ˜J˜—Jš œœ˜#šœT™TJšœ œ ˜JšœH˜HJ˜J˜—Jš œœ˜#šœT™TJšœ œ ˜JšœI˜IJ˜J˜—Jš œœ˜#šœT™TJšœ œ ˜JšœH˜HJ˜J˜—Jšœœœ˜J˜šŸ œœ œ˜AJš˜Jšœ œ$˜4Jš œœœœœ˜šœœœ ˜/Jšœ!œ˜<—Jšœ˜—J˜Jšž œ œœ˜?J˜šŸ œœE˜[Jš˜Jšœœœœ)˜@š œœœœœ˜WJšœ˜šœœ ˜J˜ J˜ J˜ Jšœœ˜——Jšœ<œ˜Ošœ œœ˜1J˜J˜ J˜J˜ —J˜-˜J˜Jšœ ž˜J˜J˜ —J˜J˜Jšœ˜Jšœœœœ œœœ˜XJšœ˜Jš œœœ œœ˜BJšœ!œ˜BJ˜ šœ˜ Jšœœœ˜(Jšœ˜J˜6Jšœ˜—Jšœ˜šœœ œ˜/J˜šœ œ ˜!J˜J˜J˜J˜'J˜J˜#J˜!J˜J˜#J˜'J˜%J˜J˜J˜J˜J˜J˜#J˜Jšœ˜—Jšœ˜—Jš œœœ œœ˜Dš˜Jšœ$˜$Jšœ)œ˜Išœ˜Jšœ(˜(Jšœ'˜'Jšœ˜Jšœ)˜)Jšœ&˜&Jšœ ˜ Jšœ˜J˜Jšœ œœ˜/—Jšœ˜Jšœ˜—Jšœ˜Jšœœ œ˜-Jšœœ˜Jšœ˜J˜—šŸœœœœ˜)Jš œœœœ4œœ˜_J˜—šŸœœœœœœœœœ œ˜dJšœ˜ Jš œœœœœ˜Jšœœœœ˜!šœœœœ˜Jšœœœ7˜JJšœ"˜"Jšœ˜šœ˜ Jšœ˜Jšœœ˜'Jšœ œ˜$Jš˜—Jšœ˜—Jšœ˜Jšœœ˜1Jšœ˜J˜—šŸ œœ(œœœœœœœœœœ˜}Jšœ˜ Jš œœœœœ˜3Jšœœ˜š˜šœœ˜Jšœ?œ˜KJšœ:œ˜H—Jšœœ˜šœ˜ šœœ ˜Jšœ?œ˜M—Jšœ2œ œœ˜I—šœ˜ Jšœ5œ˜?——Jšœ˜Jšœœœ˜Jšœ˜J˜—šŸœœœ˜&Jš˜J˜Jšœ˜J˜J˜J˜—Jšž)˜)J˜Jšœœœ˜ J˜šœœ œœ˜&Jšœœ˜J˜J˜ J˜J˜Jšœœ˜"Jšœœ˜Jšœœ˜Jšœœœ˜J˜—˜Jš˜Jšœ œ˜J˜J˜6Jšœ˜J˜—šŸ œœ œœ˜)JšžL˜LJšœ[™[J˜Jšœ œ ˜˜,Jšœ4œ œ˜J—Jšœœ˜"Jšœœ˜Jšœœ˜š Ÿ œœ œ"œœ˜MJšœ œ œœ˜'J˜˜J˜ šœ&œ˜+J˜—J˜ J˜Jšœœ˜ Jšœœ˜ J˜—Jšœœ œœ˜1Jšœœ œœ ˜:J˜—šŸ œœœ˜BJšœ˜J˜Jšœ œ˜šœ&˜&šœ4œ˜9Jš œœœœœ˜AJšœœ˜Jšœ)˜)—Jšœœ˜—J˜!˜˜*Jšœœ˜—Jšœ.œ œ˜B—J˜J˜Jšœ˜ J˜—šŸœœ œ œ˜-J˜˜šœ&œ˜,J˜—Jšœœ˜—Jšœœ œœ˜1Jšœœ œœ ˜:J˜—šŸœœ˜ J˜˜šœœ˜ J˜—Jšœœ˜—J˜!J˜J˜J˜—˜Jšž%˜%˜&šœ.œ˜4J˜—Jšœœœ œ˜5—J˜J˜J˜—J˜˜#šœ\˜\Jšœœ˜J˜—Jšœœ˜—J˜!J˜#J˜˜/J˜$Jšœœœ˜Jšœœ˜ J˜1—J˜#Jšœœ˜Jšœœœ˜$Jšœœœ˜$Jšœœœœ˜*Jšœœœœ˜)Jšœœœ˜'Jšœœœ˜)Jšœ œœ˜*Jšœ.˜.Jšœœœ˜'J˜#J˜#J˜J˜J˜—J˜%šœT™TJšœœ ˜0šœ ˜Jšœ&œ˜+Jšœ(œ"œ˜TJšœ œ˜—Jšœœ˜J˜J˜—Jšœ œœ ˜!J˜šœ œœ˜Jšœœœ˜J˜Jšœ œœ˜Jšœ œœ˜ Jšœœœœ˜J˜—šœœ˜Jš œ+œœ œœœ˜SJ˜—šŸœœ˜&Jšœ œ˜Jšœœœœ˜Jšœœœœ˜Jšœœ˜Jšœ œœœ˜J˜Jšœœ˜Jšœœ˜Jšœ%œœ˜9šœœ˜'š œœœœœœœ˜