-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.
-- HeapScanTool.mesa
-- HGM, 19-Nov-84 16:10:19
-- Brenda Hankins 12-Sep-84 17:12:43
-- might try adding ability to specify ObjectDirFile (or rebuild it if have chain) but do this later.
DIRECTORY
File USING [File, nullFile],
FormSW USING [
AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem,
DisplayItem, newLine, nextPlace, NumberItem, ProcType, StringItem],
Heap USING [systemZone],
HeapFile USING [ChainBlock, headerSize, noSegment, segmentsPerPage, segmentSize, SerialAndHead],
HeapXDefs USING [ObjectHeader, PageHeader],
Inline USING [LongCOPY, LowHalf],
MFile USING [Error, GetLength, Handle, ReadOnly, Release],
ObjectDirDefs USING [ObjectNumber, ObjectType],
Put USING [CR, Decimal, Line, LongDecimal, Text],
Space USING [InsufficientSpace, Interval, Kill, Map, nullInterval],
SpecialMFile USING [GetCapaWithAccess],
Tool USING [
Create, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc, UnusedLogName],
ToolWindow USING [TransitionProcType],
VMDefs USING [PageIndex,PageNumber, pageSize],
Window USING [Box, Handle];
HeapScanTool: PROGRAM
IMPORTS FormSW, Heap, Inline, MFile, Put, Space, SpecialMFile, Tool
SHARES HeapFile, ObjectDirDefs =
BEGIN
-- TYPES:
CompactPageFormat: TYPE = RECORD[
offset: LONG CARDINAL,
hdrs: SEQUENCE length: [0..VMDefs.pageSize] OF HeapXDefs.ObjectHeader ];
CompactHeapFormat: TYPE = RECORD[
pages: SEQUENCE length: [1..LAST[CARDINAL]] OF
LONG POINTER TO CompactPageFormat];
--VARIABLES:
toolData: MACHINE DEPENDENT RECORD [
-- tool window stuff:
msgSW(0): Window.Handle ← NIL,
fileSW(2): Window.Handle ← NIL,
formSW(4): Window.Handle ← NIL,
-- vars:
heapFileName(6): LONG STRING,
chainFileName(8): LONG STRING,
segmentNo(10): CARDINAL,
logicalSegNo(11): CARDINAL,
segmentBound(12): CARDINAL, -- last valid real segment no.
logicalVsReal(13): BOOLEAN,
chain(14): LONG POINTER TO HeapFile.ChainBlock,
chainSpace(16): Space.Interval,
objNoPage(20): CARDINAL,
objNoType(21): CARDINAL,
objNoIndex(22): CARDINAL,
compactHeapFormat(23): LONG POINTER TO CompactHeapFormat];
--****************************************************************************
OpenFiles: FormSW.ProcType =
BEGIN
heapFileHandle: MFile.Handle ← NIL;
BEGIN ENABLE
BEGIN
MFile.Error => CONTINUE;
Space.InsufficientSpace =>
BEGIN
Put.Line[toolData.msgSW, "Space.InsufficientSpace (need 6 pages)."L];
IF heapFileHandle # NIL THEN MFile.Release[heapFileHandle];
CONTINUE;
END;
END;
--looks for the files in Mesa File System.
heapFile: File.File;
heapSize, chainSize: CARDINAL;
IF toolData.heapFileName # NIL THEN
BEGIN
heapFileHandle ← MFile.ReadOnly[toolData.heapFileName, [NIL, NIL] !
MFile.Error =>
Put.Line[toolData.msgSW, "MFile error on open of data file."L] ];
heapFile ← SpecialMFile.GetCapaWithAccess[heapFileHandle];
Put.Line[toolData.fileSW, "Data file opened."L];
heapSize ← Inline.LowHalf[ MFile.GetLength[heapFileHandle]/512--bytes/pg--];
chainSize ←
((heapSize/HeapFile.segmentSize)
+ HeapFile.segmentsPerPage-1) /
HeapFile.segmentsPerPage;
toolData.segmentBound ← (heapSize/HeapFile.segmentSize) +
chainSize*HeapFile.headerSize - 1;
CreateDataStructure[heapSize, heapFile];
END
ELSE Put.Line[toolData.msgSW, "No data file specified."L];
IF heapFileHandle # NIL AND toolData.chainFileName # NIL THEN
BEGIN
tempSize: CARDINAL ← 0;
chainFileHandle: MFile.Handle ← MFile.ReadOnly[
toolData.chainFileName, [NIL, NIL] !
MFile.Error =>
Put.Line[toolData.msgSW, "MFile error on open of chain file."L] ];
chainFile: File.File ← SpecialMFile.GetCapaWithAccess[chainFileHandle];
Put.Line[toolData.fileSW, "Chain file opened."L];
IF heapSize > chainSize*2 THEN Put.Line[toolData.msgSW,
"Warning: size of chainfile is larger than that of Heap."L];
IF heapSize < chainSize*2 THEN Put.Line[toolData.msgSW, -- must stop
"Size of chainfile is smaller than that of Heap - you should quit."L];
-- build chain data structure:
toolData.chainSpace ← Space.Map[
window: [file: File.nullFile, base: 0, count: chainSize]];
toolData.chain ← toolData.chainSpace.pointer;
FOR index: CARDINAL IN [0..chainSize) DO
tempSpace: Space.Interval;
chain0, chain1, chainChoice:
LONG POINTER TO HeapFile.SerialAndHead;
-- look at the chain in duplicate page pairs and use the 'correct' one.
tempSpace ← LOOPHOLE[Space.Map[
window: [chainFile, index*2+1, 2], access: readOnly]];
-- remember to skip over leader page
chain0 ← LOOPHOLE[tempSpace.pointer];
chain1 ← LOOPHOLE[chain0+VMDefs.pageSize];
chainChoice ←
IF chain1.serialNumber > chain0.serialNumber THEN chain1 ELSE chain0;
Inline.LongCOPY[from: chainChoice, nwords: VMDefs.pageSize,
to: toolData.chain + index*VMDefs.pageSize];
Space.Kill[tempSpace];
ENDLOOP;
IF toolData.chain.header[0].chainHead = HeapFile.noSegment THEN
Put.Line[toolData.msgSW,
"There are no segments in chain (head pts to end)."L];
FOR s: CARDINAL ← toolData.chain.header[0].chainHead, toolData.chain.next[s]
UNTIL s = HeapFile.noSegment DO
IF tempSize > toolData.segmentBound THEN
{ Put.Line[toolData.fileSW, "Chain has a circularity, fix the file with the ChainCruiser tool prior to attempting to do any logical operations."L];
EXIT };
tempSize ← tempSize+1;
ENDLOOP;
IF chainFileHandle # NIL THEN MFile.Release[chainFileHandle];
END
ELSE Put.Line[toolData.msgSW, "No chain file specified."L];
-- cleanup:
IF heapFileHandle # NIL THEN MFile.Release[heapFileHandle];
END; -- enabled
END; -- proc. OpenFiles
CreateDataStructure: PROCEDURE[heapSize: CARDINAL, heapFile: File.File] =
BEGIN
segmentSpace: Space.Interval ← Space.nullInterval;
objHdrSize: CARDINAL ← SIZE[HeapXDefs.ObjectHeader];
toolData.compactHeapFormat ← Heap.systemZone.NEW[CompactHeapFormat[heapSize]];
FOR segment: CARDINAL ← 3 -- first real segment --, segment+1
UNTIL segment > toolData.segmentBound DO
position: CARDINAL;
toolData.segmentNo ← segment;
position ← Position[]; -- actual file page (1..length)
IF position = HeapFile.noSegment THEN LOOP;
segmentSpace ← Space.Map[window: [heapFile, position, 6], access: readOnly];
FOR page: CARDINAL ← 0, page+1 UNTIL page = 6 DO
noOfHdrsOnThisPage, hdrCounter: CARDINAL ← 0;
pagePtr: LONG POINTER ← (segmentSpace.pointer+page*256);
currentPos: VMDefs.PageIndex ← SIZE[HeapXDefs.PageHeader];
offset: LONG POINTER TO LONG CARDINAL ← pagePtr;
hdr: LONG POINTER TO HeapXDefs.ObjectHeader ← pagePtr + currentPos;
DO -- UNTIL end of page
noOfHdrsOnThisPage ← noOfHdrsOnThisPage+1;
currentPos ← currentPos + objHdrSize;
IF currentPos+hdr.size+objHdrSize > LAST[VMDefs.PageIndex] THEN EXIT
ELSE currentPos ← currentPos + hdr.size;
hdr ← pagePtr + currentPos;
ENDLOOP;
toolData.compactHeapFormat[position+page] ←
Heap.systemZone.NEW[ CompactPageFormat[noOfHdrsOnThisPage] ];
-- reset values to loop again:
pagePtr ← (segmentSpace.pointer+page*256);
currentPos ← SIZE[HeapXDefs.PageHeader];
hdr ← pagePtr + currentPos;
toolData.compactHeapFormat[position+page].offset ← offset↑;
DO -- UNTIL end of page
toolData.compactHeapFormat[position+page].hdrs[hdrCounter] ← hdr↑;
currentPos ← currentPos + objHdrSize;
IF currentPos+hdr.size+objHdrSize > LAST[VMDefs.PageIndex] THEN EXIT
ELSE currentPos ← currentPos + hdr.size;
hdr ← pagePtr + currentPos;
hdrCounter ← hdrCounter+1;
ENDLOOP;
ENDLOOP;
Space.Kill[segmentSpace];
ENDLOOP;
END; -- proc. CreateDataStructure
--****************************************************************************
ShowObjects: FormSW.ProcType =
BEGIN
position: CARDINAL ← Position[];
IF position = HeapFile.noSegment THEN RETURN;
-- Search the pages for (and print out) all object headers in segment.
FOR page: CARDINAL ← 0, page+1 UNTIL page = 6 DO
Put.Text[toolData.fileSW, "Page Number "L];
Put.Decimal[toolData.fileSW, position+page]; -- ignore leader page
Put.Text[toolData.fileSW, " (in file)"L];
Put.CR[toolData.fileSW];
FOR i: CARDINAL IN [0..toolData.compactHeapFormat[position+page].length) DO
PrintObjectHdr[toolData.compactHeapFormat[position+page].hdrs[i]];
IF i = 0 THEN -- first obj.
{ Put.Text[toolData.fileSW, " offset: "L];
Put.LongDecimal[toolData.fileSW,
toolData.compactHeapFormat[position+page].offset] };
Put.CR[toolData.fileSW];
ENDLOOP;
ENDLOOP;
END;
--~~~~~~~~
CheckSegmentNumber: PROCEDURE [of: CARDINAL] RETURNS [ok: BOOLEAN ← TRUE] =
BEGIN -- checks to see that it's a valid segment number.
SELECT of MOD VMDefs.pageSize FROM
0, 1, 2 => ok ← FALSE;
ENDCASE;
IF ok THEN IF of > toolData.segmentBound THEN ok ← FALSE;
IF NOT ok THEN Put.Line[toolData.msgSW, "Invalid Segment Number"L];
END;
Position: PROCEDURE RETURNS [CARDINAL] =
BEGIN -- this must take into account diff in size of page numbers.
IF toolData.logicalVsReal THEN
{ toolData.segmentNo ← LogicalToReal[];
FormSW.DisplayItem[toolData.formSW, tIndex.segmentNo.ORD] };
IF toolData.segmentNo = HeapFile.noSegment OR
(~toolData.logicalVsReal AND ~CheckSegmentNumber[toolData.segmentNo])
THEN RETURN[HeapFile.noSegment];
RETURN[
( ( toolData.segmentNo
- ( (toolData.segmentNo+VMDefs.pageSize)/VMDefs.pageSize)
* (HeapFile.headerSize))
* HeapFile.segmentSize)
+ 1 -- skip leader page --];
END;
LogicalToReal: PROC RETURNS [real: CARDINAL ← HeapFile.noSegment] =
BEGIN -- head of chain is logical 1.
counter: CARDINAL ← 1;
FOR s: CARDINAL ← toolData.chain.header[0].chainHead, toolData.chain.next[s]
UNTIL s = HeapFile.noSegment DO
IF counter > toolData.segmentBound THEN
{ Put.Line[toolData.fileSW, "Error, chain needs to be fixed."L]; EXIT };
IF counter = toolData.logicalSegNo THEN { real ← s; EXIT };
counter ← counter+1;
ENDLOOP;
END;
--~~~~~~~~
PrintObjectHdr: PROCEDURE[objectHdr: HeapXDefs.ObjectHeader] =
BEGIN -- [ size: y no: [page no: a type: b pageIndex: c] ]
Put.Text[toolData.fileSW, " [ size: "L];
Put.Decimal[toolData.fileSW, objectHdr.size];
Put.Text[toolData.fileSW, " no: [page no: "L];
Put.Decimal[toolData.fileSW, objectHdr.number.page];
Put.Text[toolData.fileSW, " type: "L];
Put.Decimal[toolData.fileSW, objectHdr.number.type.ORD];
Put.Text[toolData.fileSW, " pageIndex: "L];
Put.Decimal[toolData.fileSW, objectHdr.number.index];
Put.Text[toolData.fileSW, "] ]"L];
END;
--****************************************************************************
NextSegment: FormSW.ProcType =
BEGIN
IF toolData.logicalVsReal THEN
{ toolData.segmentNo ← toolData.chain.next[toolData.segmentNo];
toolData.logicalSegNo ← toolData.logicalSegNo+1;
FormSW.DisplayItem[toolData.formSW, tIndex.logicalSegNo.ORD] }
ELSE
BEGIN
toolData.segmentNo ← toolData.segmentNo+1;
SELECT toolData.segmentNo MOD VMDefs.pageSize FROM
0 => toolData.segmentNo ← toolData.segmentNo+3;
1 => toolData.segmentNo ← toolData.segmentNo+2;
2 => toolData.segmentNo ← toolData.segmentNo+1;
ENDCASE;
END;
FormSW.DisplayItem[toolData.formSW, tIndex.segmentNo.ORD];
IF toolData.segmentNo = HeapFile.noSegment OR
toolData.segmentNo > toolData.segmentBound THEN
Put.Line[toolData.msgSW, "No more segments."L]
ELSE ShowObjects[];
END;
--****************************************************************************
ShowSegmentContents: FormSW.ProcType =
BEGIN
position: LONG CARDINAL ← Position[]*256;
IF position = HeapFile.noSegment THEN RETURN; -- bad segmentNo.
Put.Text[toolData.fileSW, "This cmmd should show the actual bits in the segment but is unimplemented, use the OctalReadTool to look at the six pages at word "L];
Put.LongDecimal[toolData.fileSW, position]; -- word position in file
Put.CR[toolData.fileSW];
-- might later want to display bits myself.
END;
--****************************************************************************
FindObjHdr: FormSW.ProcType =
BEGIN -- take obj hdr and search thru all segments for instances of it.
ENABLE Space.InsufficientSpace =>
{ Put.Line[toolData.msgSW, "Space.InsufficientSpace (need 6 pages)."L];
CONTINUE };
objHdrSize, tempID: CARDINAL;
match: ObjectDirDefs.ObjectNumber;
SearchSegment: PROCEDURE =
BEGIN
position: CARDINAL;
toolData.segmentNo ← tempID;
FormSW.DisplayItem[toolData.formSW, tIndex.segmentNo.ORD];
position ← Position[];
IF position = HeapFile.noSegment THEN RETURN; -- bad SegmentNo.
FOR page: CARDINAL ← 0, page+1 UNTIL page = 6 DO
FOR i: CARDINAL IN [0..toolData.compactHeapFormat[position+page].length) DO
IF toolData.compactHeapFormat[position+page].hdrs[i].number = match THEN
BEGIN
Put.Text[toolData.fileSW, "Page Number "L];
Put.Decimal[toolData.fileSW, position+page]; -- ignore leader page
Put.CR[toolData.fileSW];
PrintObjectHdr[toolData.compactHeapFormat[position+page].hdrs[i]];
IF i = 0 THEN -- first obj.
{ Put.Text[toolData.fileSW, " offset: "L];
Put.LongDecimal[toolData.fileSW,
toolData.compactHeapFormat[position+page].offset] };
Put.CR[toolData.fileSW];
END;
ENDLOOP;
ENDLOOP;
END; -- proc. SearchSegment
IF toolData.objNoIndex > VMDefs.PageNumber.LAST OR
toolData.objNoType > ObjectDirDefs.ObjectType.LAST.ORD THEN
{ Put.Line[toolData.msgSW, "Illegal Obj number."L]; RETURN };
objHdrSize ← SIZE[HeapXDefs.ObjectHeader];
match ← [toolData.objNoPage, 0, LOOPHOLE[toolData.objNoType], toolData.objNoIndex];
IF toolData.logicalVsReal THEN -- search in chain order
BEGIN
toolData.segmentNo ← LogicalToReal[];
FormSW.DisplayItem[toolData.formSW, tIndex.segmentNo.ORD];
toolData.logicalSegNo ← toolData.logicalSegNo-1; --so display will be correct
FOR tempID ← toolData.segmentNo, toolData.chain.next[tempID]
UNTIL tempID = HeapFile.noSegment DO
toolData.logicalSegNo ← toolData.logicalSegNo+1;
FormSW.DisplayItem[toolData.formSW, tIndex.logicalSegNo.ORD];
SearchSegment[];
ENDLOOP
END
ELSE -- search thru file in order of pages:
FOR tempID ← toolData.segmentNo, tempID+1
UNTIL tempID > toolData.segmentBound DO SearchSegment[]; ENDLOOP;
END; -- proc. FindObjHdrs
--****************************************************************************
CheckForLogicalAllowed: FormSW.ProcType =
BEGIN
IF toolData.chain # NIL THEN RETURN;
toolData.logicalVsReal ← FALSE;
FormSW.DisplayItem[sw, index];
Put.Line[toolData.msgSW, "Logical values not allowed w/o Chain File."L];
END;
--**************************************************************************
HelpProc: FormSW.ProcType =
BEGIN
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "Specify file name(s) and bug OpenFiles to set up session. (There is no corresponding CloseFiles as deactivating the tool will clean up everything)"L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "You need not have a chain file. But if you do, then can specify logical segment indices as well as real (so can step thru only what's on chain when searching and in chain order) 1 is logical head of chain (corresponding real segment indices will be displayed in Real Segments field)."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "This tool claims to work on only Grapevine databases."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "SegmentNo identifies the place in the file that any operation will start. IF LogicalvsReal is true, the LogicalSegmentNo will be used to find the value that corresponds to that logical position in the chain (clearly must have a chain file to use this) and the corresponding real segment no. will be displayed in RealSegmentNo field. If it is not true, then RealSegmentNo will be used. WARNING: it's not clear that use of and updating of these two fields (logical and real) is completely correct at moment so pay careful attention to what you are doing and inspect all results. Notify me (BLH) if you notice any problems."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "ShowObjects will display all the object headers in the six pages of the given segment number. A brief explanation of the format follows this description."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "NextSegment will increment the segment number (either next in file or next on chain) and do a ShowObjects."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "ShowSegmentContents will show the actual bits in the given segments pages."L];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "FindObjectHdrs will search (from given segment number) for all object headers which match the specified values (the following three fields in the formSW) and print out their location in file."L];
Put.CR[toolData.fileSW];
Put.CR[toolData.fileSW];
Put.Line[toolData.fileSW, "Object Header Format:"L];
Put.Line[toolData.fileSW, " offset: the word offset into the object that the first word"L];
Put.Line[toolData.fileSW, " of this sub object represents (0 is objectStart)."L];
Put.Line[toolData.fileSW, " size: number of words (of this object) to follow"L];
Put.Line[toolData.fileSW, " in this same page (determines length of subObj and if"L];
Put.Line[toolData.fileSW, " it doesn't fill up the page, another subObj will follow)."L];
Put.Line[toolData.fileSW, " objNo:"L];
Put.Line[toolData.fileSW, " pageNo: page in ObjDir table."L];
Put.Line[toolData.fileSW, " type: type of object, check an interface for def'n of types."L];
Put.Line[toolData.fileSW, " (MS 18-22, CHS 6-12)"L];
Put.Line[toolData.fileSW, " pageIndex: index into page in ObjDir table."L];
END; -- proc. HelpProc
--**************************************************************************
-- Tool needed routines:
ClientTransition: ToolWindow.TransitionProcType =
BEGIN
SELECT TRUE FROM
new = active =>
BEGIN
toolData.heapFileName ← NIL;
toolData.chainFileName ← NIL;
toolData.segmentNo ← 3; -- real seg which corresponds to 1st data page
toolData.logicalSegNo ← 1; -- head of chain.
toolData.logicalVsReal ← FALSE;
toolData.chainSpace ← Space.nullInterval;
toolData.chain ← NIL;
toolData.objNoPage ← 0;
toolData.objNoType ← 0;
toolData.objNoIndex ← 0;
toolData.compactHeapFormat ← NIL;
END;
old = active =>
BEGIN
IF toolData.chainSpace # Space.nullInterval THEN
Space.Kill[toolData.chainSpace];
IF toolData.compactHeapFormat # NIL THEN
BEGIN
FOR i: CARDINAL IN [1..toolData.compactHeapFormat.length] DO
IF toolData.compactHeapFormat.pages[i] # NIL THEN
Heap.systemZone.FREE[@toolData.compactHeapFormat.pages[i]];
ENDLOOP;
Heap.systemZone.FREE[@toolData.compactHeapFormat];
END;
END;
ENDCASE;
END; -- proc. ClientTransition
--***************************************************************************
MakeTool: PROCEDURE [initialBox: Window.Box] =
BEGIN
window: Window.Handle ← Tool.Create[
makeSWsProc: MakeSWs, initialState: default,
initialBox: initialBox, clientTransition: ClientTransition,
name: "HeapScanTool"L, tinyName1: "Heap"L, tinyName2: "ScanTool"L];
END; -- proc. MakeTool
--******************************************************************************
tIndex: TYPE = {heapFileName, chainFileName, openFiles, segmentNo,
logicalSegNo, logicalVsReal, showObjects, nextSegment,
showSegmentContents, help, findObjHdr, objNoPage, objNoType,
objNoIndex};
noToolIndices: CARDINAL = tIndex.LAST.ORD+1;
MakeCommon: FormSW.ClientItemsProcType =
BEGIN OPEN FormSW;
items ← AllocateItemDescriptor[noToolIndices];
items[tIndex.heapFileName.ORD] ← StringItem [tag: "Data File Name"L,
place: newLine, string: @toolData.heapFileName, inHeap: TRUE];
items[tIndex.chainFileName.ORD] ← StringItem [tag: "Chain File Name"L,
place: newLine, string: @toolData.chainFileName, inHeap: TRUE];
items[tIndex.openFiles.ORD] ←
CommandItem [tag: "OpenFiles"L, proc: OpenFiles, place: newLine];
items[tIndex.segmentNo.ORD] ← NumberItem[tag: "Real Segment No"L,
value: @toolData.segmentNo, radix: decimal, place: newLine];
items[tIndex.logicalSegNo.ORD] ← NumberItem[tag: "Logical Segment No"L,
value: @toolData.logicalSegNo, radix: decimal, place: nextPlace];
items[tIndex.logicalVsReal.ORD] ←
BooleanItem [tag: "Logical vs Real"L, switch: @toolData.logicalVsReal,
proc: CheckForLogicalAllowed, place: nextPlace];
items[tIndex.showObjects.ORD] ←
CommandItem [tag: "Show Objects"L, proc: ShowObjects, place: newLine];
items[tIndex.nextSegment.ORD] ←
CommandItem [tag: "Next Segment"L, proc: NextSegment, place: nextPlace];
items[tIndex.showSegmentContents.ORD] ←
CommandItem [tag: "Show Segment Contents"L, proc: ShowSegmentContents, place: nextPlace];
items[tIndex.help.ORD] ←
CommandItem [tag: "Help"L, proc: HelpProc, place: nextPlace];
items[tIndex.findObjHdr.ORD] ←
CommandItem [tag: "FindObjHdr"L, proc: FindObjHdr, place: newLine];
items[tIndex.objNoPage.ORD] ← NumberItem[tag: "ObjNoPage"L,
value: @toolData.objNoPage, radix: decimal, place: nextPlace];
items[tIndex.objNoType.ORD] ← NumberItem[tag: "ObjNoType"L,
value: @toolData.objNoType, radix: decimal, place: nextPlace];
items[tIndex.objNoIndex.ORD] ← NumberItem[tag: "ObjNoIndex"L,
value: @toolData.objNoIndex, radix: decimal, place: nextPlace];
RETURN[items: items, freeDesc: TRUE];
END; -- proc. MakeCommon.
--****************************************************************************
MakeSWs: Tool.MakeSWsProc =
BEGIN
logName: STRING ← [40];
Tool.UnusedLogName[unused: logName, root: "HeapScanTool.log"L];
toolData.msgSW ← Tool.MakeMsgSW[window: window, h: 32];
toolData.formSW ←
Tool.MakeFormSW[window: window, formProc: MakeCommon, h: 96];
toolData.fileSW ← Tool.MakeFileSW[window: window, name: logName, h:350];
END;
--************************************************************************
-- mainline code:
MakeTool[[[0, 35],[482,650]]]; -- change size
END.
LOG:
4-Sep-84 12:31:29 converted from MSScanTool