-- 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