/*----------------------------------------------------------------------------
** Name: linenum.cpp
**
** Title: Line Number Table
**
** Purpose:
**  Create and manage the table that holds all module line numbers
**    Provides:
**       AddLinenum
**       GetLinenum
**       LinenumEnd
**       LinenumStart
**
** Base class = MemPool
**
** Status: TESTED
**
** $Log:   S:/tbird/mt2_186/symbol/linenum.cpv  $
** 
**    Rev 1.4   04 Jun 1998 14:20:04   hera
** 
**    Rev 1.3   02 Sep 1997 16:46:58   Judy
** To fix line number bug. Modify LinenumTable::AddLineNum(). 
** (Test report id: 104, 105)
** 
**    Rev 1.2   22 Jul 1997 10:10:20   Judy
** 
**    Rev 1.1   26 Feb 1997 11:42:54   Judy
** 
**    Rev 1.1   14 Jan 1997 15:30:48   Judy
** No change.
** 
**    Rev 1.0   14 Jun 1996 16:42:28   Judy
** Initial revision.
** 
**    Rev 1.53   07 Jun 1995 10:37:32   nghia
** Revised to cleanup LinenumTable initialization.
** Revised LinenumEnd() to reset the linenumLoadState to NOT_LOADING.  Added
** check for linenumLoadState in LinenumStart().
** 
**    Rev 1.52   10 Feb 1995 09:25:08   nghia
** Added SymGetLastLinenum() and GetLastLinenum().
** 
**    Rev 1.51   12 Jan 1995 17:34:02   nghia
** Added initialized the nextIndex value for SymGetLineNumberByIndex().
** 
**    Rev 1.50   10 Jan 1995 09:53:28   nghia
** Updated for PV 2.4 - Revised LinenumEnd() to check for invalid module
** address range and use the start and end line addresses for the module range.
** 
**    Rev 1.49   03 Jan 1995 13:48:10   nghia
** Updated for PV 2.4 - cleanup compiler warning on unused variable.
** 
**    Rev 1.48   07 Dec 1994 14:48:28   nghia
** Merged PV 2.2e (rev 1.39.1.1) changes to PV 2.4 (rev 1.47).
** 
**    Rev 1.47   10 Oct 1994 17:36:36   nghia
** Fixed bug in LinenumEnd() - sort all line numbers before adding the last one.
** Revised the checking for modules having just one line number.
** 
**    Rev 1.46   29 Sep 1994 09:19:10   nghia
** Put send linenum sorting message in debug mode.
** 
**    Rev 1.45   26 Sep 1994 10:39:44   nghia
** Revised SymGetLinenum routines to map the module address to correct symbol
** addressing mode before setting address offset value.
** 
**    Rev 1.44   22 Aug 1994 18:34:40   brucea
** Changed name of global var NoPublics to FilterPublics
** 
**    Rev 1.43   04 Aug 1994 18:26:52   brucea
** Added a cli message for sorting linenums and the number being sorted.
** NOTE: this code creates an error message box when run from the loader dialog.
** 
**    Rev 1.42   03 Aug 1994 11:59:18   brucea
** Modified: ::AddLinenum to force saving the last dummy linenum if its
**    value is the same as the previous.  Since this was added, the code
**    was changed to make the dummy linenum one greater than the last, but
**    leaving this code in does no harm.
** Changed: ::GetRawLinenumByIndex so that the displaysymbols lines will display
** the last dummy linenum
** Modified: ::LinenumEnd code which checked for the max addr before incrementing
**    it by one.  Now if real mode, compares to 0xFFFF
** Modified: SymMapAddr2LinenumModule and ...StmtModule to set the global
**   FilterPublics to cause an address lookup to ignore publics
** Added: UpdateLinenumEndAddr to modify the last (dummy) linenum when a modified
**   module address comes in from the loader.
** 
**    Rev 1.41   28 Jul 1994 13:10:54   brucea
** Added sorting of linenums
** 
**    Rev 1.40   20 Jun 1994 09:55:56   nghia
** Added GetLineNumbersList() to extract a list of linenumbers from the symbol
** table.
** 
**    Rev 1.39.1.1   07 Dec 1994 14:09:24   nghia
** Fixed GPF bug when the first loaded module does not have line # information.
** TestFile: o:\customer\cpu32\unisys\demo.x.
** Initialized previousLinenumOffset to 0L to avoid unknown value when there
** is no line numbers added.  Test previousLinenumOffset <= linenumOffsetStart
** to determine whether or not line numbers added.  Test linenumOffsetEnd >
** linenumOffsetStart to find out if line numbers already loaded.
** 
**    Rev 1.39.1.0   18 Nov 1994 04:29:00   nghia
** PV 2.2e - Revised LinenumEnd() to handle adjusting module and address ranges
** to whichever range that can cover both the module and start/end lines.
** Copy linenumCompare() from PV 2.3 to here for linenumber sorting.
** 
**    Rev 1.39   08 Mar 1993 11:14:16   nghia
** Fixed bugs: column number zero sometimes, test to look for linenumber 
** statement will fail if all lines have column 255.
** 
**    Rev 1.38   18 Dec 1992 18:18:22   brucea
** Fixed: ::GetLinenumStmt by initing leftColumn and priorLeftColumn to 0
**    so that code will, first pass, store offset into greaterThanOffset
** 
**    Rev 1.37   08 Dec 1992 07:07:10   brucea
** Fixed: bug in reporting column info when requested column beyond any in
**    storage (in LinenumTable::GetLinenumStmt); fixed default value for
**    greaterThanOffset
** 
**    Rev 1.36   08 Nov 1992 13:48:44   nghia
** Added SymGetLinenumInOrder() and GetLinenumInOrder() to fix ppr 7517 and 7520
** These routines are private to the Source Presenter only.
** 
**    Rev 1.35   22 Oct 1992 18:18:52   brucea
** Redesigned and implemented ::GetLinenumStmt to handle column requests
**    greater than the largest column number stored and to correct problems with
**    the line and column returned with multiple columns for one line and
**    column values that are out of order in the by-address sorted linenum table.
** 
**    Rev 1.34   13 Aug 1992 19:25:32   brucea
** Fixed: GetLinenumFirstLast to use correct start-end linenum pointers
**        GetLinenumFirstIndex - removed call to UpdateCacheParams which
**        auto-loaded linenums
** 
**    Rev 1.33   13 Aug 1992 11:03:14   brucea
** Changed: 0xFFFFFFFF to 0xFFFFFFFFL
** 
**    Rev 1.32   12 Aug 1992 09:45:10   brucea
** Bug fix: GetLinenumByIndex was not returning correct nextIndex when there
**    are multiple same linenums
** 
** 
**    Rev 1.31   10 Aug 1992 09:18:52   brucea
** Added: check for moduleDesc == NULL_SYMBOL in UpdateCacheParams
** Added: check for valid module descriptor at beginning of SymGetLinenum,
**    SymGetLinenumByIndex, SymGetLinenumStmt, SymGetLinenumStmtByIndex,
**    SymMapAddr2Linenum, SymMapAddr2LinenumStmt, SymMapAddr2LinenumModule, 
**    SymMapAddr2LinenumModuleStmt 
** 
**    Rev 1.30   06 Aug 1992 22:06:18   brucea
** Removed: functions ::AreThereLinenums and IsModuleCached (in .h) and
**    rolled both into ::UpdateCacheParams
** Removed: calls to IsModuleCached and AreThereLinenums
** Modified: ::GetLinenumFirstLast to check if lines are loaded and return
**    err if true rather than call UpdateCacheParams which auto loaded lines
** 
**    Rev 1.29   06 Aug 1992 10:08:02   brucea
** Changed access to .typeIndex.symType to use &0xF
** 
**    Rev 1.28   31 Jul 1992 18:49:08   brucea
** Added: test for linenums loaded in SymMapAddr2LinenumModule and 
**    SymMapAddr2LinenumStmtModule
** 
**    Rev 1.27   27 Jul 1992 23:29:28   brucea
** Fixed bug: in ::AddLinenum to remove later line with same address
** Added: module count class variable
** Changed: return of line number count to take into account the number of
**    modules loaded (and subtract extra linenum added for each module).
** Fixed bug: ::GetLinenumByIndex; a boundary problem getting index of
**    very first linenum in list.
** Removed: private class var <duplicateLinenums>
** Added: EXPORT function GetRawLinenumByIndex, GetLinenumFirstIndex
** 
**    Rev 1.26   25 Jul 1992 16:38:32   brucea
** Added: ::AreThereLinenums, a check to see if linenums exist for the passed
**    module descriptor
** Added: checks for lines in all the callable interfaces that needed it
** 
**    Rev 1.25   19 Jul 1992 21:29:56   brucea
** Modified: ::AddLinenum algorithm.  If sequential linenums have the same
**    address, the second is overwritten on top of the first.  This makes the
**    source/asm browser place all source lines with the same code address
**    above the asm for those lines.  This also reduces the number of linenums
**    that need to be stored
** Modified: ::GetLinenumByIndex - removed requirement for linenum to be greater
**    than start address of found linenum when searching for the end address
**    of the linenum-by-index
** Bug fix: ::LinenumEnd  The dummy linenum added is now given and address one
**    more than the end address of the module so that searching for an
**    address range will find the last address of the last linenum
** 
**    Rev 1.24   10 Jul 1992 19:06:38   brucea
** Bug fix: moved linenumLoadState = NOT_LOADING; to end of function after
**    AddLinenum(lastLinenumVar) so that the AddLinenum wouldn't fail.
** 
**    Rev 1.23   21 Apr 1992 16:52:48   brucea
** Added: LinenumTable::ObliterateTable in order to re-initialize private vars
**    after symbol_removesymbols has occurred.
** Moved: In LinenumTable::LinenumEnd, moved the setting of linenumLoadState to
**    top of function so that it will always be done.  Before it was after
**    a function that could create an error and never return.
** 
**    Rev 1.22   20 Apr 1992 08:53:12   brucea
** Enhanced: code now skips a linenum being loaded if it is identical to the
**    previous linenum (same linenum, column num, address)
** Algorithm change: during a linenum lookup, the end address was calculated as
**    the next linenum's address - 1.  However, it is possible that there are
**    different linenums with the same code address.  Therefore, the end address
**    is calculated as the next linenum with a larger address.
** Added: GetRawLinenumByIndex, LinenumTable::GetLinenumFirstIndex - used to
**    display linenums in CLI
** 
**    Rev 1.21   01 Apr 1992 23:36:36   brucea
** Fixed: compile warnings
** Added: SymMapAddr2LinenumStmt
** 
**    Rev 1.20   30 Mar 1992 18:17:16   brucea
** Fixed: boundary error when searching backward in linenum list.  Now check for
**    index <= linenumOffsetStart before decrementing index in
**    ::GetLinenumByIndex
** 
**    Rev 1.19   20 Mar 1992 14:41:24   brucea
** Fixed: bug in calculated the end address of a linenum; occurs in several
**    places
** 
**    Rev 1.18   18 Mar 1992 13:21:12   brucea
** Fixed bug in ::MapAddr2Linenum: passing address of columnRange->columnStart
**    in call to GetLinenumByIndex
** 
**    Rev 1.17   16 Mar 1992 13:53:26   brucea
** Changed: usage of MAX_COLUMN to SYM_MAX_COLUMN
** 
**    Rev 1.16   12 Mar 1992 22:20:30   brucea
** Changed: end address points to last byte of range; for linenums, the end
**   address is the address of the next linenum MINUS 1.
**   This is reflected in ::GetLinenum, ::GetLinenumByIndex, ::GetLinenumStmt
**   ::GetLinenumStmtByIndex
** Added: lineType to ::MapAddr2Linenum, made column into columnRange; altered
** Modified: calls to MapAddr2Linenum to reflect different interface
** Created: SymMapAddr2LinenumStmtModule to support statement-level lookup by
**    addr
** 
** 
**    Rev 1.15   27 Feb 1992 22:41:36   brucea
** Fixed: bug in binary search in LinenumTable::MapAddr2Linenum
** 
**    Rev 1.14   10 Jan 1992 15:52:50   brucea
** Fixed bug in GetLinenumFirstLast - didn't return last VALID linenum
** 
**    Rev 1.13   20 Dec 1991 17:05:56   brucea
** Fixed binary search routine in MapAddr2Linenum
** 
**    Rev 1.12   20 Dec 1991 15:46:40   brucea
** Fixed bug in SymMapAddr2LinenumModule - call to SymMapAddr2Symbol
** was passing the second param as an input constant rather than as
** an output (return) parameter
** 
**    Rev 1.11   20 Dec 1991 15:14:36   brucea
** Cosmetic changes
** Added function LinenumTable::GetLinenumFirstLast
** Fixed bug in SymMapAddr2LinenumModule - moved UtilOnDemandLoad call
**   past SymMapAddr2Symbol
** 
**    Rev 1.10   17 Dec 1991 16:10:32   john
** Changed ::LinenumStart to accept the module descriptor for the
** module we are adding linenums to.
** Changed SymAddLinenumStart for the same reason as ::LinenumStart.
** 
**    Rev 1.9   12 Dec 1991 20:06:22   brucea
** Changed the comparison (>) back to original (correct) form in GetLinenum
** 
**    Rev 1.8   12 Dec 1991 14:59:48   brucea
** removed EXPORT from member functions
** Fixed bug in GetLinenum: inside for loop, was subtracting bigger number
** from a smaller number
** 
**    Rev 1.7   12 Dec 1991 14:23:30   brucea
** Removed code to exclude duplicate addressed linenums
** Fixed bug in GetLinenum: > becomes < for comparison of present
** linenum to requested linenum
** 
**    Rev 1.6   11 Dec 1991 16:44:26   brucea
** Comment changes, put #define max values
** Addlinenum linenum changed to lineStruct; check for NOT_LOADING state
** Convert column value of 0 to MAX_COLUMN
** In GetLinenum, changed while to for
** In all functions that used Adr calls, setting addresses are done with
** separate calls that convert offset addresses to logical addresses:
**    UtilSetAddrOffset, UtilSetEndAddrOffset
** Added GetLinenumByIndex, GetLinenumStmtByIndex
** In LinenumStart, added check for linenums already loaded
** Consolidation with function calls to SymMapAddr2Linenum and ...Module
** 
**    Rev 1.5   09 Dec 1991 17:35:54   brucea
** Changes follow:
** 
** Added code which throws away duplicate linenums (those that have same
**   address)
** Added internal function UpdateCacheParams
** Code fixes to GetLinenum
** Implemented GetLinenumStmt
** Implemented SymGetLinenumStmt
**
**    Rev 1.4   02 Dec 1991 11:22:48   brucea
** Fixed up DESCRIPTOR variable names to include Desc on end
** 
**    Rev 1.3   29 Nov 1991 20:08:30   brucea
** Added includes for: addr.h, basetbl.h symget.h
** changed COMP_ADDR_RANGE_TYPE to DESCRIPTOR
** Added calls to addr.h: AdrSetAddrOffset, AdrSetEndAddrOffset
** changed call to SymGetModuleName to SymGetModuleDesc
** changed LOGICAL_ADDR_TYPE to OFFSET_ADDR_TYPE
** changed code in SymMapAddr2Linenum to support address descriptors
** 
**    Rev 1.2   18 Nov 1991 15:36:24   brucea
** Added LinenumTable::MapAddr2Linenum
** Changed SymGetLinenumNext to SymGetLinenumByIndex
**         SymGetLinenumStmtAddr to SymGetLinenumStmt
**         SymGetLinenumStmtNext to SymGetLinenumStmtByIndex
** Implemented SymMapAddr2LinenumModule
** Changed ADDR_STRUCT to LOGICAL_ADDR_TYPE and code that used it
** 
**    Rev 1.1   13 Nov 1991 18:10:22   brucea
** Changed definition and code which implements linenumOffsetEnd to
**    point to the dummy linenum rather than one past it
** Added call to SymGetModuleName in LinenumStart
** Fixed bugs
** 
**    Rev 1.0   12 Nov 1991 10:06:56   brucea
** Initial revision.
** 
** $Header:   S:/tbird/mt2_186/symbol/linenum.cpv   1.4   04 Jun 1998 14:20:04   hera  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _BASETBL_
#include "basetbl.h"
#endif

#ifndef __STDLIB_H
#include <stdlib.h>
#endif

#ifndef _LINENUM_
#include "linenum.h"
#endif

#ifndef _MEMPOOL_
#include "mempool.h"
#endif

#ifndef _SYMCLI_
#include "symcli.h"
#endif

#ifndef _SYMGET_
#include "symget.h"
#endif

                       /****************************
                        *                          *
                        *        DEFINITIONS       *
                        *                          *
                        ****************************/

#define LINENUM_INIT_SIZE      (sizeof(LINENUM_STRUCT_TYPE) * 1024L)
#define LINENUM_EXPANSION_SIZE (sizeof(LINENUM_STRUCT_TYPE) * 1024L)
#define MAX_LINENUM_OFFSET     0xFFFFFFFFL
#define MAX_DELTA              0xFFFF
#define MAX_LINENUM            0xFFFF

// used to aid debugging address descriptors
typedef struct {
   U32 addrOffset;
   U32 rangeLength; /* number of bytes in range, if range active */
   U16 ldt;
   U16 addrSegSel;
   ADDR_TYPE type;
   ADDR_MODE mode;
   ADDR_SPACE space;
   ADDR_SEGSEL_TYPE segSelType;
   BOOLEAN rangeActive;
   BOOLEAN symbolUsage;
} AD;

// must declare one address structure for typedef to be visible
AD ad;

LinenumTable lt;       // declare static line number table object

SYM_DESCRIPTOR PreviousDesc; //Hera 5/10/97

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern MemPool st;
extern BaseIndexTable bit;    // used to determine if real mode linenums
extern BOOLEAN FilterPublics; // used to gate by-addr search to ignore

                       /****************************
                        *                          *
                        *     EXECUTABLE CODE      *
                        *                          *
                        ****************************/

//------------------------------------------------------------------------
//                  LINE NUMBER IMPLEMENTATION COMMENTS
//
// Line number sorting by address
//    It appears that line numbers are created in address assending order, at
//    least from MRI.  When loading line numbers, a <sorted> flag will be
//    maintained.  It is checked when LinenumEnd is called and only sort
//    if it is FALSE.
//
// Column information
//    A column value of 0 is converted upon a line load to the maximum
//    possible value, since a value of 0 represents the end of the line.
//
// Last line number
//    A false line number is added to the end of the line number list.
//    It is used to create an address range for the last loaded line number.
//    Its address is the end address of the module if greater than the
//    last line number, or the last line number's address + 1.
//    Its line number value is the largest value possible.  Column = max.
//    LinenumOffsetEnd is set to point to this dummy line number.
//    The following is a picture of the offset pointers relative to the
//    line numbers loaded:
//
//          linenumOffsetStart --> first linenum loaded
//                                    ........   
//                                    ........
//                                 last  linenum loaded
//          linenumOffsetEnd ----> dummy linenum (holds range info for last)
//
//------------------------------------------------------------------------


// member functions for LinenumTable

LinenumTable::LinenumTable():MemPool()        // constructor
   {
   initialSize = LINENUM_INIT_SIZE;
   expandSize  = LINENUM_EXPANSION_SIZE;
   linenumLoadState    = NOT_LOADING;
   cachedModDescriptor = NULL_DESCRIPTOR;
   linenums = 0;
   modulesLoaded = 0L;
   previousLinenumOffset = 0L;  // start offset to add line numbers
   linenumOffsetStart = linenumOffsetEnd = 0L;
};  // end of LinenumTable constructor

//------------------------------------------------------------------------
// LinenumTable::ObliterateTable
//
// Purpose:
//    Reinitialize private vars
//------------------------------------------------------------------------
VOID LinenumTable::ObliterateTable(VOID) {

   MemPool::ObliterateTable();
   initialSize = LINENUM_INIT_SIZE;
   expandSize  = LINENUM_EXPANSION_SIZE;
   linenumLoadState    = NOT_LOADING;
   cachedModDescriptor = NULL_DESCRIPTOR;
   previousLinenumOffset = 0L;   // start offset to add linenumber
   linenumOffsetStart = linenumOffsetEnd = 0L;
   linenums = 0;
   modulesLoaded = 0L;
} // end of LinenumTable::ObliterateTable


//------------------------------------------------------------------------
// LinenumTable::AddLinenum
//
// Purpose:
//    Insert a line number into the object's table
//    MRI generates the same address for multiple source lines;
//    the previous one will be written over with the current linenum
//------------------------------------------------------------------------
RETCODE
LinenumTable::AddLinenum(LINENUM_STRUCT_TYPE& lineStruct)  {

   LINENUM_STRUCT_TYPE HUGE * linenumPtr;

   if (linenumLoadState == NOT_LOADING)
      return ER_LINENUM_BAD_STATE;
   // check for sorted by address --Hera 5/9/97
   if (lineStruct.segment == previousLine.segment &&
				lineStruct.addr < previousLine.addr)  {
      sorted = FALSE;
   }
   /* Hera 6/17/97 modified by <Judy 9/2/97> to fix test report Id:104, 105 bug */
   if(lineStruct.linenum < previousLine.linenum) {
     return GOOD; // added by <Judy 9/2/97>
      /* linenumSorted = FALSE; marked by <Judy 9/2/97> */
   }

   // convert 0 value column to maximum column which it represents
   if (lineStruct.column == 0)
      lineStruct.column = SYM_MAX_COLUMN;
   // write the line and column values overtop the previous linenum that has
   // the same address as the current one
   // skip update of previous if the dummy linenum is being added
   if ((previousLine.addr == lineStruct.addr) &&
       (lineStruct.linenum != MAX_LINENUM)) { 
      if (linenums > 0) {   // at least one already loaded
         linenumPtr =
            (LINENUM_STRUCT_TYPE HUGE *)GetHugeDataPtr(previousLinenumOffset);
         linenumPtr->linenum = lineStruct.linenum;
         linenumPtr->column = lineStruct.column;
         previousLine.addr = lineStruct.addr;  // save for next compare // Judy 9/2/97
         previousLine.segment = lineStruct.segment; //Judy 9/2/97
         previousLine.linenum = lineStruct.linenum; //Judy 9/2/97
         return GOOD;
      }
   }
   // save state for next compare
   previousLine.addr = lineStruct.addr;  // save for next compare
   previousLine.segment = lineStruct.segment; //Hera 5/9/97
   previousLine.linenum = lineStruct.linenum; //Hera 6/17/97
   linenums++;  // keep a count
   // NOTES: PutBytes modifies the previousLinenumOffset value
   return PutBytes((U8 *)&lineStruct,
                   LINENUM_STRUCT_SIZE,
                   previousLinenumOffset);   // class (static) variable
}  // end of LinenumTable::AddLinenum

//------------------------------------------------------------------------
// LinenumTable::GetLinenum
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    Mark the linenum that is closest (less than or equal to) the requested
//       linenum .
//    Break out of loop if an exact match occurs
//    Fill in the return values.
//       The address range must include all addresses of line numbers that are
//       identical AND contiguous
//       (e.g. can have 3 line 20's (different columns) with
//       different start addresses).  The end address is the address of the
//       first line number not equal to the one found
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenum(SYM_DESCRIPTOR     module,
                         LINENUM_TYPE       requestedLinenum,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_TYPE       *actualLinenum,
                         COLUMN_TYPE        *actualColumn,
                         LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE         err;
   OFFSET_ADDR_TYPE startAddr, endAddr;   

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   TABLE_OFFSET offset;
   TABLE_OFFSET closestOffset;
   U16          linenumDelta;    // difference between requested linenum and
                                 // comparison linenum
   U16          tmpDelta,segment;
   LINENUM_STRUCT_TYPE HUGE*linenumPtr;//,*linenumEnd,*linenumEnd_1;

   closestOffset = MAX_LINENUM_OFFSET; // set to unattainable value so that
                                       // loop can be checked at end
   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   for (offset = linenumOffsetStart, linenumDelta = MAX_DELTA;
        offset < linenumOffsetEnd;
        offset += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);

      if ((linenumPtr->linenum) > requestedLinenum)  {
         tmpDelta = (linenumPtr->linenum) - requestedLinenum;
         if (tmpDelta < linenumDelta) {
            // closer line number, save offset and update the delta
            closestOffset = offset;
	    linenumDelta = tmpDelta;
            segment = linenumPtr->segment; //Hera 
         }
      }
      else if ((linenumPtr->linenum) == requestedLinenum)  {
	    closestOffset = offset;
            segment = linenumPtr->segment; //Hera 5/20/97
            break;  // don't need to look any further - have exact match
      }
   } // end of for

   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;
   *actualColumn  = linenumPtr->column;

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =
      UtilSetAddrOffset(module, linenumPtr->segment/*Hera 5/10/97*/,
                        startAddr, addrRangeDesc)))
      return err;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // search for the next line number not equal to the one found, or exit loop
   // if at end of line number list
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {

      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
         (((linenumPtr->addr) > startAddr) || (linenumPtr->segment != segment)/*Hera 5/20/97*/))
         break;
   }  // end of for

   // set to first entry past matching linenum with non-duplicate address
   *nextIndex = offset;

   // fill in end address; linenumPtr points to first linenum after matching
   // linenums; if dummy, the linenum is max and address is correct value

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   endAddr = linenumPtr->addr;
   //Hera 5/20/97
   if (segment != linenumPtr ->segment) {
      int i;
      COMMON_SYMBOL_HEADER HUGE* symPtr;
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(module);
      for (i = 0; i < symPtr->segNum; i++) {
	 if (segment == symPtr->segment[i]) {
	    if (symPtr->beginAddr[i] <= startAddr &&
	       startAddr <= symPtr->endAddr[i]){
	       endAddr = symPtr->endAddr[i]+1;
	       break;
	    }
	 }
      }
   }
   /*if (segment != linenumPtr->segment){
       linenumEnd = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(linenumOffsetEnd);
       linenumEnd_1 = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr((linenumOffsetEnd-LINENUM_STRUCT_SIZE));
       endAddr = startAddr + (linenumEnd->addr - linenumEnd_1->addr);
   }*/
   if (endAddr > startAddr) { // valid end address
      endAddr--;
   } else {
      // return a valid address range as well as an error
       err = UtilSetEndAddrOffset(module,(((BASE_ADDRESS)segment) << 4), startAddr, addrRangeDesc);
       return ER_SYM_INVALID_LN_END_ADDR;
   }
   return UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4), endAddr, addrRangeDesc);

}  // end of LinenumTable::GetLinenum

//------------------------------------------------------------------------
// LinenumTable::GetLastLinenum
//
// Purpose: Gets the last line number information given a module to look up
//                
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLastLinenum(SYM_DESCRIPTOR moduleDesc,
                             DESCRIPTOR     lastLinenumAddrRange,
                             LINENUM_TYPE   *lastLinenum) {
   RETCODE         err;
   LINENUM_STRUCT_TYPE HUGE* linenumPtr;
   SYM_TYPE_MODULE     HUGE* modulePtr;

   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDesc);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      // Update class vars for start and end of linenums for this module
      if ((err = UpdateCacheParams(moduleDesc)) != GOOD)
         return err;
   }
   
   // last valid linenum is the one previous to the last in table
   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)
      GetHugeDataPtr(modulePtr->linenumOffsetEnd-LINENUM_STRUCT_SIZE);
   *lastLinenum  = linenumPtr->linenum;

   // fill in the start and end address of last line number found
   if ((err = UtilSetAddrOffset(moduleDesc,
				linenumPtr->segment,/*Hera 5/10/97*/
                                linenumPtr->addr,
                                lastLinenumAddrRange)) != GOOD)
      return err;   

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)
      GetHugeDataPtr(modulePtr->linenumOffsetEnd);
   
   return UtilSetEndAddrOffset(moduleDesc,
			       (((BASE_ADDRESS)linenumPtr->segment) << 4),
                               linenumPtr->addr,
                               lastLinenumAddrRange);
}  // end of LinenumTable::GetLastLinenum

//------------------------------------------------------------------------
// LinenumTable::GetLinenumInOrder
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    Mark the linenum that is closest (less than or equal to) the requested
//       linenum.
//    Break out of loop if an exact match or a greater than match occurs
//    Fill in the return values.
//       The address range must include all addresses of line numbers that are
//       identical AND contiguous
//       (e.g. can have 3 line 20's (different columns) with
//       different start addresses).  The end address is the address of the
//       first line number not equal to the one found
//    NOTES: This routine implemented to fix PPR7517 and 7520.  Will be
//       removed when a new mixed mode data object implement.
//       This is private to Source Presenter only.
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumInOrder(SYM_DESCRIPTOR     module,
                                LINENUM_TYPE       requestedLinenum,
                                DESCRIPTOR         addrRangeDesc,
                                LINENUM_TYPE       *actualLinenum,
                                LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE         err;
   OFFSET_ADDR_TYPE startAddr, endAddr;


   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   TABLE_OFFSET offset;
   TABLE_OFFSET closestOffset;
   LINENUM_STRUCT_TYPE HUGE*linenumPtr;//,*linenumEnd, *linenumEnd_1;
   U16 segment;

   closestOffset = MAX_LINENUM_OFFSET; // set to unattainable value so that
                                       // loop can be checked at end
   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   for (offset = linenumOffsetStart; offset < linenumOffsetEnd;
        offset += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);

      if ((linenumPtr->linenum) >= requestedLinenum)  {
	 closestOffset = offset;
         segment = linenumPtr->segment; //Hera 5/20/97
         break;  // don't need to look any further - have exact match
      }
   } // end of for

   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =  UtilSetAddrOffset(module, linenumPtr->segment/*Hera 5/10/97*/,
                                         startAddr, addrRangeDesc)))
      return err;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // search for the next line number not equal to the one found, or exit loop
   // if at end of line number list
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {

      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
	 (((linenumPtr->addr) > startAddr) || (segment != linenumPtr->segment)/*Hera 5/20/97*/))
         break;
   }  // end of for

   // set to first entry past matching linenum with non-duplicate address
   *nextIndex = offset;

   // fill in end address; linenumPtr points to first linenum after matching
   // linenums; if dummy, the linenum is max and address is correct value

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   endAddr = linenumPtr->addr;
   //Hera 5/20/97
   if (segment != linenumPtr ->segment) {
      int i;
      COMMON_SYMBOL_HEADER HUGE*symPtr;
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(module);
      for (i = 0; i < symPtr->segNum; i++) {
	 if (segment == symPtr->segment[i]) {
	    if (symPtr->beginAddr[i] <= startAddr &&
	       startAddr <= symPtr->endAddr[i]){
	       endAddr = symPtr->endAddr[i]+1;
	       break;
	    }
	 }
      }
   }
   /*if (segment != linenumPtr->segment){
      linenumEnd = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(linenumOffsetEnd);
      linenumEnd_1 = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr((linenumOffsetEnd-LINENUM_STRUCT_SIZE));
      endAddr = startAddr + (linenumEnd->addr - linenumEnd_1->addr);
   }*/

   if (endAddr > startAddr) { // valid end address
      endAddr--;
   }
   else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4), startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   return UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4) ,endAddr, addrRangeDesc);
}  // end of LinenumTable::GetLinenumInOrder

//------------------------------------------------------------------------
// GetRawLinenumByIndex
//
// Purpose: return basic info on linenum; return next index.  If index
//          out of bounds, return error
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetRawLinenumByIndex(SYM_DESCRIPTOR     module,
                                   LINENUM_DESCRIPTOR index,
                                   DESCRIPTOR         linenumAddr,
                                   LINENUM_TYPE       *linenum,
                                   COLUMN_TYPE        *column,
                                   LINENUM_DESCRIPTOR *nextIndex) {

   RETCODE err;
   OFFSET_ADDR_TYPE startAddr;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
// !!! debug
   if (index > linenumOffsetEnd)
// if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE HUGE* linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   *nextIndex = index + LINENUM_STRUCT_SIZE;
   startAddr = linenumPtr->addr;   // return the start address for linenum
   return UtilSetAddrOffset(module, linenumPtr->segment /*Hera 5/10/97*/,
                            startAddr, linenumAddr);
}  // end of GetRawLinenumByIndex


//------------------------------------------------------------------------
// GetLinenumByIndex
//
// Design:
//    Check for valid index, return if not
//    Look up linenum at location <index>, set linenum, column, and nextIndex
//    Fill in addrRangeDesc
//       Search backward until the previous linenums do not match looked up
//          linenum.  Use its address as the start address.
//       Search forward until linenums do not match looked up linenum.
//       Use last found matching linenum as end address.
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumByIndex(SYM_DESCRIPTOR     module,
                                LINENUM_DESCRIPTOR index,
                                DESCRIPTOR         addrRangeDesc,
                                LINENUM_DESCRIPTOR *nextIndex,
                                LINENUM_TYPE       *linenum,
                                COLUMN_TYPE        *column)  {

   RETCODE err;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   LINENUM_DESCRIPTOR testIndex;
   U16 segment; //Hera 5/10/97

   // 01/10/95 - Nghia
   // nextIndex == 0 indicate index beyond the end of line numbers
   *nextIndex = 0;
   
   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE HUGE*linenumPtr;//,*linenumEnd, *linenumEnd_1;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   segment = linenumPtr->segment; //Hera 5/10/97
   *nextIndex = index + LINENUM_STRUCT_SIZE;
   
   // search backward to the point where the line number is not equal to the
   // one found, or exit loop if at beginning of linenum list
   testIndex = index;
   while (TRUE) {
      if (index <= linenumOffsetStart)
         break;
      testIndex = index - LINENUM_STRUCT_SIZE;
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(testIndex);
      if (linenumPtr->linenum == *linenum) {
         index -= LINENUM_STRUCT_SIZE;
      } else {
         break;  /* reached linenum not equal */
      }
   }
   // get latest linenum of <maybe> new index
   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, linenumPtr->segment,
                        startAddr, addrRangeDesc)))
      return err;

   // search forward for end of contiguous linenums
   index = *nextIndex;  // recover to one past retrieved linenum
   do {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);
      if (linenumPtr->linenum != *linenum)
         break;
      index += LINENUM_STRUCT_SIZE;
   }
   while (index < linenumOffsetEnd);  // linenumOffsetEnd points to the
                                      // created dummy linenum
   // index now pointing to first linenum beyond last matching linenum
   // with a greater address
   *nextIndex = index;  // set up next index to also be the next unique
                        // linenum
   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   //Hera 5/20/97
   if (segment != linenumPtr ->segment) {
      int i;
      COMMON_SYMBOL_HEADER HUGE* symPtr;
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(module);
      for (i = 0; i < symPtr->segNum; i++) {
	 if (segment == symPtr->segment[i]) {
	    if (symPtr->beginAddr[i] <= startAddr &&
	       startAddr <= symPtr->endAddr[i]){
	       endAddr = symPtr->endAddr[i] + 1;
	       break;
	    }
	 }
      }
   }
   /*if (linenumPtr->segment != oldSegment) {
     linenumEnd = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(linenumOffsetEnd);
     linenumEnd_1 = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr((linenumOffsetEnd-LINENUM_STRUCT_SIZE));
     endAddr = startAddr + (linenumEnd->addr - linenumEnd_1->addr);
   }*/
   if (endAddr < startAddr) { // invalid end address
       // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4) ,startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   else {
      endAddr--;
   }
   return UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4), endAddr, addrRangeDesc);
}  // end of GetLinenumByIndex


//------------------------------------------------------------------------
// LinenumTable::GetLinenumFirstLast
//
// Purpose: Returns the first and last linenum
//
// Error: returns warning if there are no linenums or they haven't been
//        loaded yet (doesn't force a load)
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumFirstLast(SYM_DESCRIPTOR     moduleDesc,
                                  LINENUM_TYPE       *firstLinenum,
                                  LINENUM_TYPE       *lastLinenum) {

   LINENUM_STRUCT_TYPE HUGE* linenumPtr;
   SYM_TYPE_MODULE     HUGE* modulePtr;
   int index;
   U32 linenumOffset;
   U16 segment;

   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDesc);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      return ER_NO_LINENUMS_ADDED;  // interpreted as no linenums in table
   }
   linenumPtr =
	 (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(modulePtr->linenumOffsetStart);
   segment = linenumPtr->segment;
   index =0;
   firstLinenum[0] = linenumPtr->linenum;
   //Hera 5/22/97
   for (linenumOffset = modulePtr->linenumOffsetStart;
      linenumOffset < modulePtr->linenumOffsetEnd; linenumOffset+=LINENUM_STRUCT_SIZE) {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(linenumOffset);
      if (segment == linenumPtr->segment)
	 lastLinenum[index] = linenumPtr->linenum;
      else {
	 segment = linenumPtr->segment;
         index++;
	 firstLinenum[index] = linenumPtr->linenum;
      }
   }   
   return GOOD;
}  // end of LinenumTable::GetLinenumFirstLast

//------------------------------------------------------------------------
// LinenumTable::GetLinenumFirstIndex
//
// Purpose: Returns the index to the first linenum in module table
//
// Error: returns warning if there are no linenums              
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumFirstIndex(SYM_DESCRIPTOR     moduleDesc,
                                   LINENUM_DESCRIPTOR FAR *nextIndex) {

   SYM_TYPE_MODULE HUGE* modulePtr;

   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDesc);
   if ((*nextIndex = (modulePtr->linenumOffsetStart)) >=
        (modulePtr->linenumOffsetEnd)) {
      return ER_NO_LINENUMS_ADDED;  // interpreted as no linenums in table
   }
   return GOOD;
}  // end of LinenumTable::GetLinenumFirstIndex


//------------------------------------------------------------------------
// LinenumTable::GetLinenumStmt
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    If the present linenum is equal to the requested one, toggle a flag
//       and reset rightColumn to large number.  The flag is used to ignore
//       anymore linenums that are not equal to requested linenum.
//    When a linenum is found closer than the previous (but not equal to),
//       want the matching statement to be the closest to the beginning of
//       the line; i.e. the smallest column matching that line.  This means
//       that the requested column is actually 1 for this case.
//    Mark the entry with the secondary condition (assuming linenun delta is
//       the same as a previous entry) with a column value that is closer yet
//       greater than or equal to the requested column.
//    Mark the entry whose column is closest to the requested column value and
//       also less than or equal to the requested column.
//    Must scan through ALL line numbers because there can be more than one
//       close or equal linenum out of order that can be closer to the
//       requested column.
//    Fill in the return values.
//       The address range is only the range of the single entry. The end
//       address is the address of the next entry unless the same, then it
//       is the first linenum after with a different address.
//    The column range is surrounding column points that contain the requested
//       column.  The left column is initialized to 1 and is the largest
//       column value less than the requested column.  The right column is
//       initialized to smallest column value greater than or equal to the
//       requested column.  A column value of 0 is the same as SYM_MAX_COLUMN
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumStmt(SYM_DESCRIPTOR     module,
                             LINENUM_TYPE       requestedLinenum,
                             COLUMN_TYPE        requestedColumn,
                             DESCRIPTOR         addrRangeDesc,
                             LINENUM_TYPE       *actualLinenum,
                             COLUMN_RANGE_TYPE  *actualColumnRange,
                             LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE  err;
   OFFSET_ADDR_TYPE startAddr, endAddr;

   TABLE_OFFSET offset;

   // set to unattainable value so that loop can be checked at end
   TABLE_OFFSET closestOffset = MAX_LINENUM_OFFSET;

   LINENUM_STRUCT_TYPE HUGE* linenumPtr;//,*linenumEnd,*linenumEnd_1;
   // difference between requested linenum and comparison linenum
   U16         linenumDelta = MAX_DELTA;
   U16         tmpDelta,segment;
   COLUMN_TYPE leftColumn,
               priorLeftColumn,       // holds column before last column if
                                      // requested column past last column
               rightColumn,
               localColumn;           // local store of column being evaluated
   BOOLEAN     reqLessThanaColumnFound;  // requested column less than or
                                         // equal to stored columns found
   TABLE_OFFSET greaterThanOffset;    // represents offset of linenum on
                                      // matching line that has column closest
                                      // but less than requested column
   BOOLEAN     firstTimeOnReqLine = FALSE;
   // transition from not on line to on line.  Only TRUE for one entry;
   // used to reset rightColumn

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   offset = linenumOffsetStart;

   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   // init column value and state holders
   leftColumn = 0;  // init to be less than smallest column so first pass
                    // will cause greaterThanOffset to be updated
   priorLeftColumn = 0;   // same
   rightColumn = SYM_MAX_COLUMN;     // init right column marker
   reqLessThanaColumnFound = FALSE;
   greaterThanOffset = MAX_LINENUM_OFFSET; // default to largest so will fail
                                           // if no match found
   while (offset < linenumOffsetEnd)  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);
      if (requestedLinenum <= (linenumPtr->linenum)) {
         tmpDelta = (linenumPtr->linenum) - requestedLinenum;
         localColumn = linenumPtr->column;

         if (tmpDelta == 0)  { // on the requested line
            linenumDelta = 0;  // set so that < comparison always fails
            if (!firstTimeOnReqLine) {
               rightColumn = SYM_MAX_COLUMN;
               firstTimeOnReqLine = TRUE;
            }
            if (localColumn >= requestedColumn) {
               // requested column is possibly within boundary of statement
               // note: local columns point to last valid char of statement
               reqLessThanaColumnFound = TRUE;
               if (rightColumn >= localColumn) {
                  // new entry has column closer to requested column than
                  // previous found
                  rightColumn = localColumn;
                  closestOffset = offset;  // save linenum with closer column
               }
            } else { // requested column is beyond column looked up
               // if new column looked up is closer to requested column,
               // save it and the linenum offset; it may become the
               // returned linenum if nothing closer shows up
               // Must keep track of second largest column; it defines the
               // end of the previous stmt which defines the left column
               // Cannot block out new columns less than previous column
               // because it could be the previous stmt.  This occurs because
               // columns can be out of order; e.g. 25, 41, 35
               // We want to end up with leftColumn the biggest column,
               // priorLeftColumn the next biggest
               if (localColumn > leftColumn) {
                  if (leftColumn > priorLeftColumn)
                     priorLeftColumn = leftColumn;
                  leftColumn = localColumn;  // slide left column closer to
                                             // requested column
                  greaterThanOffset = offset;
               } else {
                  if (localColumn > priorLeftColumn) {
                     priorLeftColumn = localColumn;
                  }
               }
            }
         // the next two else ifs are finding the closest column to
         // column 1
         } else if (tmpDelta < linenumDelta) { // a  closer match
            // init for new line
            leftColumn = 1;
            rightColumn = localColumn; // init right column marker to
                                       // end of this statement
            reqLessThanaColumnFound = TRUE;  // valid column data
            closestOffset = offset;  // save closer linenum

            // set the local requested column var copy to column 1 if not
            // matching requested line
            linenumDelta = tmpDelta;  // save closer approximation
         } else if (tmpDelta == linenumDelta) {
            // same "not equal" linenum, different column; move toward right
            if (localColumn < rightColumn) {
               rightColumn = localColumn;
               closestOffset = offset;  // save closer linenum
            }
         }  // end of if (tmpDelta == 0
      }  // end of if (requestedLinenum <= (linenumPtr->linenum))

      offset += LINENUM_STRUCT_SIZE;  // move to next linenum entry
   } // end of while

   if (reqLessThanaColumnFound == FALSE) {
      closestOffset = greaterThanOffset;  // go back to closest if < not found
      leftColumn = priorLeftColumn;       // start of column making up last
                                          // stored column
   }
   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(closestOffset);

   *actualLinenum = linenumPtr->linenum;
   segment = linenumPtr->segment; //Hera 5/20/97
   // fill in start address of line number found
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, linenumPtr->segment,
                         startAddr, addrRangeDesc)))
      return err;

   // columns point to end of stmt; need to bump by one except for column 1
   if (leftColumn > 1)
      leftColumn++;
   actualColumnRange->columnStart = leftColumn;
   actualColumnRange->columnEnd = rightColumn;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // fill in end address of statement found
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);
      if ((linenumPtr->addr) > startAddr || segment != linenumPtr->segment)
         break;
   }  // end of for

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   //Hera 5/20/97
   if (segment != linenumPtr ->segment) {
      int i;
      COMMON_SYMBOL_HEADER HUGE* symPtr;
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(module);
      for (i = 0; i < symPtr->segNum; i++) {
	 if (segment == symPtr->segment[i]) {
	    if (symPtr->beginAddr[i] <= startAddr &&
	       startAddr <= symPtr->endAddr[i]){
	       endAddr = symPtr->endAddr[i] + 1;
	       break;
	    }
	 }
      }
   }
   /*if (segment ! = linenumPtr->segment){
      linenumEnd = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(linenumOffsetEnd);
      linenumEnd_1 = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr((linenumOffsetEnd-LINENUM_STRUCT_SIZE));
      endAddr = startAddr + (linenumEnd->addr - linenumEnd_1->addr);
   }*/
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4) ,startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   return UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4), endAddr, addrRangeDesc);
}  // end of LinenumTable::GetLinenumStmt


//------------------------------------------------------------------------
// GetLinenumStmtByIndex
//
// Design:
//    Check for valid index, return if not
//    Look up linenum at location <index>, set linenum, the nextIndex,
//       and fill in addrRangeDesc
//    Fill in end columnRange = column of the retrieved linenum.
//    Fill in the starting column:
//       Initialize starting column to 1.
//       From beginning to end of linenum list:
//          if (entry = retrieved linenum) && (column < retrieved column)
//             set starting column to max(present value, column value)
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumStmtByIndex(SYM_DESCRIPTOR     module,
                                    LINENUM_DESCRIPTOR index,
                                    DESCRIPTOR         addrRangeDesc,
                                    LINENUM_TYPE       *linenum,
                                    COLUMN_RANGE_TYPE  *columnRange,
                                    LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE err, firstErr = GOOD;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   TABLE_OFFSET offset;
   U16 segment;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE HUGE* linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   columnRange->columnEnd  = linenumPtr->column;  // marks the end point
   segment = linenumPtr->segment; //Hera 6/2/97
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, linenumPtr->segment,
			startAddr, addrRangeDesc)))
      return err;

   *nextIndex = index + LINENUM_STRUCT_SIZE;

   // fill in end address of statement found
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);
      if (linenumPtr->segment != segment || (linenumPtr->addr) > startAddr)
         break;
   }  // end of for

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   //Hera 5/20/97
   if (segment != linenumPtr ->segment) {
      int i;
      COMMON_SYMBOL_HEADER HUGE* symPtr;
      symPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(module);
      for (i = 0; i < symPtr->segNum; i++) {
	 if (segment == symPtr->segment[i]) {
	    if (symPtr->beginAddr[i] <= startAddr &&
	       startAddr <= symPtr->endAddr[i]){
	       endAddr = symPtr->endAddr[i] + 1;
	       break;
	    }
	 }
      }
   }//Hera
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4) ,startAddr, addrRangeDesc);
      firstErr = ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   err = UtilSetEndAddrOffset(module, (((BASE_ADDRESS)segment) << 4), endAddr, addrRangeDesc);

   // get the start column by finding the closest column value to the end
   // column on the same line
   for (index = linenumOffsetStart, columnRange->columnStart = 1;
        index < linenumOffsetEnd;
        index += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(index);
     
      if ((linenumPtr->linenum) == *linenum)  {
         if (linenumPtr->column < columnRange->columnEnd)  {
         columnRange->columnStart = max(columnRange->columnStart,
                                        linenumPtr->column);
         }
      }
   }  // end of for
   // columns point to last character of stmt; need to increment to point
   // to first char of next stmt but only if not first char
   if (columnRange->columnStart > 1)
      (columnRange->columnStart)++;
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
}  // end of GetLinenumStmtByIndex


//------------------------------------------------------------------------
// LinenumTable::LinenumEnd()
//
// Purpose: Close the line number loading for a specific module
//
// Input parameters:
//
// Output parameters:
//
// Error: Generates error if no line numbers have been loaded
//
// PseudoCode:
//    Register the start and end pointers with owner module
//    If no line numbers have been added, return that error code
//    Insert a final dummy line number into table.  Its address is the end
//       address of the module if larger than the previous line number, or 
//       one greater than the previous if not.  Its line number value is
//       the largest value possible.
//    If not already sorted, sort linenum list by address
//------------------------------------------------------------------------
RETCODE
LinenumTable::LinenumEnd()  {
   TABLE_OFFSET        offset, tmpBaseOffset;
   LINENUM_STRUCT_TYPE HUGE* lastLinenumPtr, HUGE* firstLinenumPtr;
   SYM_TYPE_MODULE     HUGE* modulePtr;
   LINENUM_STRUCT_TYPE lastLinenumVar;
   RETCODE             err;
   BaseSymbol          HUGE* basePtr;

   // test to see if any line numbers have been added
   // if the (previousLinenumOffset > the linenumOffsetStart) then
   // there are line numbers of a module is added.
   // Check !lt.linenums to avoid that there is only one linenum
   // in one module and previousLinenumOffset=0 <added by Eric 7/11/97>
   if ((previousLinenumOffset <= linenumOffsetStart) && (!lt.linenums)) {
      // store linenum pointers into module structure
      modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(cachedModDescriptor);
      modulePtr->linenumOffsetStart = linenumOffsetStart;
      modulePtr->linenumOffsetEnd = linenumOffsetEnd;
      // 06/06/95 - Reset linenumLoadState
      linenumLoadState = NOT_LOADING;
      return ER_NO_LINENUMS_ADDED;
   }

   // update the end pointer of the line numbers (points to location where
   // the last line number will was added)
   linenumOffsetEnd = previousLinenumOffset;

    // 10/06/94 - Nghia
   // sort all line numbers in the list first, then adding in the dummy line.
   // This will ensure that the last line number is in address order.
   //Hera 6/17/97
   if (!linenumSorted) {
      qsort(GetHugeDataPtr(linenumOffsetStart),  // starting linenum
            (U16)(((linenumOffsetEnd-linenumOffsetStart)/LINENUM_STRUCT_SIZE)
                    + 1),           // number of linenums to sort
	    LINENUM_STRUCT_SIZE,    // size of each linenum struct
            LinenumCompare1
	    );
   }
   //Hera
   if (!sorted)  { // sort list
      LINENUM_STRUCT_TYPE HUGE *startPtr;
      LINENUM_STRUCT_TYPE HUGE *endPtr;
      TABLE_OFFSET startOffset,endOffset;
#ifdef DEBUG      
      S8 str[40];
      sprintf(str, "Sorting %d line numbers...",
       (U16)(((linenumOffsetEnd-linenumOffsetStart)/LINENUM_STRUCT_SIZE + 1)));
      SendMessageToCli((LPU8)str);
#endif
      for (startOffset = linenumOffsetStart; startOffset <= linenumOffsetEnd;) {         
	 startPtr = (LINENUM_STRUCT_TYPE HUGE *)GetHugeDataPtr(startOffset);
	 for (endOffset = (startOffset+LINENUM_STRUCT_SIZE);
	                                  endOffset <= linenumOffsetEnd ;) {
	    endPtr = (LINENUM_STRUCT_TYPE HUGE *)GetHugeDataPtr(endOffset);
	    if (startPtr->segment == endPtr->segment)
	       endOffset = endOffset + LINENUM_STRUCT_SIZE;
	    else {          
	       qsort(GetHugeDataPtr(startOffset),
		  (U16)((endOffset-startOffset)/LINENUM_STRUCT_SIZE),
		   LINENUM_STRUCT_SIZE,
		   LinenumCompare);
	       startOffset = endOffset;
	       break;
	    }            
	  }
	  if (endOffset > linenumOffsetEnd) {
	     qsort(GetHugeDataPtr(startOffset),
		  (U16)((endOffset-startOffset)/LINENUM_STRUCT_SIZE),
		   LINENUM_STRUCT_SIZE,
		   LinenumCompare);
	     startOffset = endOffset;
          }
      }
      // Note: linenumOffsetEnd points to dummy linenum added, but it should
      // be included in the sort, hence the + 1 for the size.
   }

   // update the end pointer of the line numbers (points to location where
   // the dummy line number will be added)
   linenumOffsetEnd = GetAvailMemOffset();
   // store linenum pointers into module structure
   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(cachedModDescriptor);
   modulePtr->linenumOffsetStart = linenumOffsetStart;
   modulePtr->linenumOffsetEnd = linenumOffsetEnd;

   firstLinenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(linenumOffsetStart);
   
   // If module does not valid address range, used the start address of the
   // first line number and the last line number
   // adjust the start address of module if < start line address
   if ((modulePtr->symHeader.symHeader.beginAddrInfo.startAddr >
          modulePtr->symHeader.symHeader.endAddrInfo.endAddr) ||
       (firstLinenumPtr->addr <
          modulePtr->symHeader.symHeader.beginAddrInfo.startAddr)) {
      modulePtr->symHeader.symHeader.beginAddrInfo.startAddr =
         firstLinenumPtr->addr;
   }
   
   // get offset to last added line number, then address of that linenum
   offset = linenumOffsetEnd - LINENUM_STRUCT_SIZE;
   lastLinenumPtr = (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(offset);

   // if module end address is not valid, used the last line number address
   if (!modulePtr->symHeader.symHeader.endAddrInfo.endAddr) {
      lastLinenumVar.addr =
         modulePtr->symHeader.symHeader.endAddrInfo.endAddr =
            lastLinenumPtr->addr;
   } else {
      // build up dummy line number, then store away.
      // First, set the address of the dummy line number to the end
      // address of the parent module or the last linenum, whichever
      // is greater.  Then increment it by 1 to make it point to next
      // ficticious linenum so that the correct range is maintained for the
      // last linenum.  Only increment by 1 if the value is not the max
      // offset value possible.  This is 8 F's for Moto and Intel protected
      // 32, and 4 F's for Intel real and protected 16.  The last is not
      // check for since the symbol table does not contain 16/32 protected
      // information.
      //Hera 5/19/97
      int index = modulePtr->symHeader.symHeader.segNum - 1;
      if (index >= 0) {	 
         while (index >= 0) {
	    if (lastLinenumPtr->segment ==
			    modulePtr->symHeader.symHeader.segment[index]) {
			              
	       lastLinenumVar.addr =
                 max(modulePtr->symHeader.symHeader.endAddr[index],
		     lastLinenumPtr->addr);
               index = -1;
	    }
            index--;
	 }//Hera
      }
      else {
	  lastLinenumVar.addr =
	     max(modulePtr->symHeader.symHeader.endAddrInfo.endAddr,
                 lastLinenumPtr->addr);
      }
   }
   // increase by 1 if not at end of address range
   // NOTE: !!! this code checks for Intel real mode to use 0xFFFF as max
   // offset.  It does not take into account Intel protected 16 mode
   // since this is not detectable from symbol info.
   if((err = bit.GetOffset(modulePtr->symHeader.symHeader.typeIndex.baseIndex,
                           tmpBaseOffset)) != GOOD)
      return(err);
   // get pointer to BaseSymbol, then check for real mode
   basePtr = (BaseSymbol HUGE*)st.GetHugeDataPtr(tmpBaseOffset);
   if (((SYM_REAL == (basePtr->GetSymMode())) &&    // real-mode segment
        ((lastLinenumVar.addr) < 0xFFFFL)) ||
       ((lastLinenumVar.addr) < 0xFFFFFFFFL)) {
      lastLinenumVar.addr++;
   }
   lastLinenumVar.linenum = MAX_LINENUM;
   lastLinenumVar.column = SYM_MAX_COLUMN;
   if ((err = AddLinenum(lastLinenumVar)) != GOOD)
      return err;
   linenumLoadState = NOT_LOADING;
   //Hera 5/23/97
   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(cachedModDescriptor);
   if (modulePtr->symHeader.child == 0L) {
      modulePtr->symHeader.symHeader.segNum--;
      modulePtr->symHeader.symHeader.segment[modulePtr->symHeader.symHeader.segNum] = 0;
      modulePtr->symHeader.symHeader.beginAddr[modulePtr->symHeader.symHeader.segNum] = 0;
      modulePtr->symHeader.symHeader.endAddr[modulePtr->symHeader.symHeader.segNum] = 0;
   }
   //Hera
   return GOOD;
}  // end of LinenumTable::LinenumEnd


//------------------------------------------------------------------------
// LinenumTable::LinenumStart
//
// Purpose: Starts, or opens, the addition of line numbers to selected
//          module
// Design:
//    Get module; register its SYM_DESCRIPTOR with the LT
//    Initialize start and end pointers
//------------------------------------------------------------------------
RETCODE
LinenumTable::LinenumStart(SYM_DESCRIPTOR moduleDescriptor)  {

   SYM_TYPE_MODULE HUGE* modulePtr;

   // 06/06/95 - Nghia
   // Added check for invalid linenum loading state
   if (linenumLoadState == LOADING)
      return ER_LINENUM_BAD_STATE;

   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDescriptor);
   if (modulePtr && 
      (modulePtr->linenumOffsetEnd > modulePtr->linenumOffsetStart))  {
      // TRUE means this module already has linenums loaded
      return ER_LINENUMS_ALREADY_LOADED;
   }
   cachedModDescriptor = moduleDescriptor;
   linenumOffsetStart = linenumOffsetEnd = GetAvailMemOffset();
   
   // set defaults
   sorted = TRUE;
   linenumSorted = TRUE; //Hera 6/17/97
   previousLine.addr = 0L;
   previousLine.linenum = 0;
   previousLine.column = 0;
   previousLine.segment = 0; //Hera 5/9/96
   linenums = 0;

   modulesLoaded++;
   linenumLoadState = LOADING;
   return GOOD;
}  // end of LinenumTable::LinenumStart


//------------------------------------------------------------------------
// LinenumTable::MapAddr2Linenum
//
// Purpose:
//    Gets the line number information given an address and module descriptor
//    The address range returned is the range of addresses of the same
//       contiguous linenum looked up
//
// Design:
//    Perform binary search on linenum list
//    Mark the linenum entry whose address range encloses the requested
//       address ( >= linenum address AND < next linenum addresss)
//    Break out of loop if an exact match occurs
//    Fill in the return values.
//       The address range is the range of only the line number that contains
//       the requested address.
//       Fill in index of linenum found
//------------------------------------------------------------------------
RETCODE
LinenumTable::MapAddr2Linenum(SYM_LINE_TYPE      lineType,
                              U16                segment,
                              OFFSET_ADDR_TYPE   requestedAddr,
                              SYM_DESCRIPTOR     module,
                              LINENUM_TYPE       *linenum,
                              COLUMN_RANGE_TYPE  *columnRange,
                              DESCRIPTOR         addrRangeDesc,
                              LINENUM_DESCRIPTOR *index)  {

   RETCODE err;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   BOOLEAN found;
   S32 bottom, middle, top;  // table indices
   LINENUM_STRUCT_TYPE HUGE* linenumPtr, HUGE* nextLinenumPtr;

   // normalize all linenum table indices by dividing by the size of a
   // linenum structure
   bottom = linenumOffsetStart / LINENUM_STRUCT_SIZE;

   // the end of the list of linenums is one record less than the end pointer
   // because the end pointer points to the dummy linenum
   top = (linenumOffsetEnd - LINENUM_STRUCT_SIZE) / LINENUM_STRUCT_SIZE;

   found = FALSE;
   //Hera 5/13/97
   while (top >= bottom) {
       linenumPtr =
       (LINENUM_STRUCT_TYPE HUGE*)GetHugeDataPtr(top * LINENUM_STRUCT_SIZE);
       if (segment != (linenumPtr->segment))
	  top--;
       else if(requestedAddr >= linenumPtr->addr) {
	  nextLinenumPtr = (LINENUM_STRUCT_TYPE HUGE*)
	     GetHugeDataPtr((top+1) * LINENUM_STRUCT_SIZE);
	  if(requestedAddr < nextLinenumPtr->addr ||
	     linenumPtr->segment != nextLinenumPtr->segment /*Hera 5/20/97*/) {
	     found = TRUE;
	     break;
          }
	  else
	     top--;
       }
       else
          top--;
   }
   middle = top;

   /*while (top >= bottom)  {
      middle = (top + bottom) / 2;
      linenumPtr =
      (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(middle * LINENUM_STRUCT_SIZE);

      // test for requestedAddr < *middle linenum addr
      if (requestedAddr < (linenumPtr->addr))  {
         top = middle - 1;
      }
      // test for requestedAddr > *middle next linenum addr
      // NOTE: compare is >= because the end address is represented by the
      // next line number (start) address.  The size returned should subtract
      // one from that address when returning the range.
      // i.e. address of linenum entry - address of (entry + 1) - 1 =
      //   address range of the linenum
      else  {
         nextLinenumPtr = (LINENUM_STRUCT_TYPE *)
            GetHugeDataPtr((middle + 1) * LINENUM_STRUCT_SIZE);
         if (requestedAddr >= (nextLinenumPtr->addr))  {
            bottom = middle + 1;
         } else {
            // a match was found
            found = TRUE;
            break;
         }
      }
   }  // end of while*/

   if (!found)
      return ER_ADDRESS_NOT_FOUND;

   // fill in parameters
   if (lineType == SYM_LINE) {
      return GetLinenumByIndex(module,
                               (middle * LINENUM_STRUCT_SIZE),
                                     // offset to found linenum
                               addrRangeDesc,
                               index,
                               linenum,
                               &(columnRange->columnStart));
   } else { //
      return GetLinenumStmtByIndex(module,
                                   (middle * LINENUM_STRUCT_SIZE),
                                      // offset to found linenum
                                   addrRangeDesc,
                                   linenum,
                                   columnRange,
                                   index);
   }

}  // end of LinenumTable::MapAddr2Linenum


//------------------------------------------------------------------------
// LinenumTable::UpdateCacheParams
//
// Purpose:
//    fills in the cached module descriptor and start and end offsets
//    into the linenum table for the requested module, but only if
//    the requested module descriptor is different.
//    Checks that there are linenums and tries to get them (on-demand)
//    if not loaded.  Generates error if not linenums available.
//------------------------------------------------------------------------
RETCODE
LinenumTable::UpdateCacheParams(SYM_DESCRIPTOR moduleDesc)  {

   SYM_TYPE_MODULE HUGE* modulePtr;
   RETCODE         err;

   if (linenumLoadState == LOADING)
      LinenumEnd();  // should never occur if loader makes the correct call
                     // sequences
   // get module start-end indices to line number table if requested module
   // different than cached module descriptor
   if (moduleDesc != cachedModDescriptor)  {
      if (NULL_SYMBOL == moduleDesc) {
         return ER_SYMBOL_NOT_A_MODULE;
      }
      modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDesc);
      if (((modulePtr->symHeader.symHeader.typeIndex.symType) & 0xF)
         != SYM_MODULE)
         return ER_SYMBOL_NOT_A_MODULE;
      cachedModDescriptor = moduleDesc;
      linenumOffsetStart = modulePtr->linenumOffsetStart;
      linenumOffsetEnd   = modulePtr->linenumOffsetEnd;
   }

   // now check that linenums are loaded
   if (linenumOffsetStart >= linenumOffsetEnd) {
      // linenums not yet loaded
      if (GOOD != (err = UtilOnDemandLoad(moduleDesc))) {
         return err;
      }

      // now get cached params
      modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(moduleDesc);
      if (((modulePtr->symHeader.symHeader.typeIndex.symType) & 0xF)
         != SYM_MODULE) {
         return ER_SYMBOL_NOT_A_MODULE;
      }
      linenumOffsetStart = modulePtr->linenumOffsetStart;
      linenumOffsetEnd   = modulePtr->linenumOffsetEnd;

      // check once again for valid linenums
      if (linenumOffsetStart >= linenumOffsetEnd) {
         return ER_NO_LINENUMS_ADDED;
      }
   }
   return GOOD;
} // end of UpdateCacheParams

//------------------------------------------------------------------------
// LinenumTable::GetLineNumbersList
//
// Purpose: Retrieve a list of line numbers of <module>, started from
// <startIndex>.  Fill the descriptor in <linenumList> return the actual
// returned descriptor, excluding the <startIndex>, Stop if reach the
// <numRequested> lines.
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLineNumbersList(SYM_DESCRIPTOR          module,
                                 LINENUM_DESCRIPTOR      startIndex,
                                 U16                     numRequested,
                                 LINENUM_DESCRIPTOR FAR* linenumList,
                                 U16 FAR*                actualReturned){

   RETCODE err;
   LINENUM_DESCRIPTOR index = startIndex;

   *actualReturned = 0;
   // update class vars for start and end of linenums for this module
   if ((err = UpdateCacheParams(module)) != GOOD)
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   // retrieve all index
   while ((index < linenumOffsetEnd) && ((*actualReturned) < numRequested)) {
      index += LINENUM_STRUCT_SIZE;
      linenumList[*actualReturned] = index;
      ++(actualReturned); 
   }
   // done
   return GOOD;
}


#if 0
//------------------------------------------------------------------------
// LinenumTable::AreThereLinenums
//
// Purpose:
//    Checks that there were linenums for this module.
//
// Return parameter:
//   TRUE ==> module contains linenums
//   FALSE ==> module does not contain linenums
//------------------------------------------------------------------------
BOOLEAN LinenumTable::AreThereLinenums(SYM_DESCRIPTOR module) {

   SYM_TYPE_MODULE HUGE* modulePtr;

   modulePtr = (SYM_TYPE_MODULE HUGE*)st.GetHugeDataPtr(module);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      return FALSE;
   } else {
      return TRUE;
   }
}  // end of AreThereLinenums
#endif

                       /****************************
                        *                          *
                        *   NON-MEMBER FUNCTIONS   *
                        *                          *
                        ****************************/
#ifdef __cplusplus
extern "C" {
#endif

//------------------------------------------------------------------------
// SymAddLinenum
//
// Design:
//    Insert a line number into the lt table
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenum(LINENUM_TYPE     linenum,
	      U8               column,
              U16              segment,
              OFFSET_ADDR_TYPE addr)  {

   LINENUM_STRUCT_TYPE  linenumVar;
   linenumVar.addr = addr;
   linenumVar.linenum = linenum;
   linenumVar.column = column;
   linenumVar.segment = segment;
   return lt.AddLinenum(linenumVar);
}  // end of SymAddLinenum


//------------------------------------------------------------------------
// SymAddLinenumEnd
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenumEnd()  {
   
   return lt.LinenumEnd();   // close the line number entries for module
}  // end of SymAddLinenumEnd


//------------------------------------------------------------------------
// SymAddLinenumStart
//
// Purpose: Starts, or opens, the addition of line numbers to selected
//          module
// Design:
//    Get module; register its SYM_DESCRIPTOR with the LT
//    Initialize start and end pointers
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenumStart(SYM_DESCRIPTOR moduleDescriptor)  {

   // open the line number entries for module
   return lt.LinenumStart(moduleDescriptor);
}  // end of SymAddLinenumStart


//------------------------------------------------------------------------
// SymGetLinenum
//
// Design:  Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenum(SYM_DESCRIPTOR     moduleDesc,
              LINENUM_TYPE       linenum,
              DESCRIPTOR         addrRangeDesc,
              LINENUM_TYPE       *actualLinenum,
              COLUMN_TYPE        *actualColumn,
              LINENUM_DESCRIPTOR *nextIndex)  {
   RETCODE err;
      
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND; 
   if ((err = SymGetSymbolAddress(moduleDesc, addrRangeDesc)) != GOOD)
      return err;
   return (lt.GetLinenum(moduleDesc,
                         linenum,
                         addrRangeDesc,
                         actualLinenum,
                         actualColumn,
                         nextIndex));
}  // end of SymGetLinenum

//------------------------------------------------------------------------
// SymGetLinenumInOrder
//
// Design:  Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumInOrder(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_TYPE       linenum,
                     DESCRIPTOR         addrRangeDesc,
                     LINENUM_TYPE       *actualLinenum,
                     LINENUM_DESCRIPTOR *nextIndex)  {
   RETCODE err;
   
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;
   if ((err = SymGetSymbolAddress(moduleDesc, addrRangeDesc)) != GOOD)
      return err;

   return (lt.GetLinenumInOrder(moduleDesc,
                         linenum,
                         addrRangeDesc,
                         actualLinenum,
                         nextIndex));
}  // end of SymGetLinenumInOrder

//------------------------------------------------------------------------
// SymGetLinenumByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumByIndex(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_DESCRIPTOR index,
                     DESCRIPTOR         addrRangeDesc,
                     LINENUM_DESCRIPTOR *nextIndex,
                     LINENUM_TYPE       *linenum,
                     COLUMN_TYPE        *column)  {
   RETCODE err;
   
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;
   if ((err = SymGetSymbolAddress(moduleDesc, addrRangeDesc)) != GOOD)
      return err;
   return lt.GetLinenumByIndex(moduleDesc,
                               index,
                               addrRangeDesc,
                               nextIndex,
                               linenum,
                               column);
}  // end of SymGetLinenumByIndex


//------------------------------------------------------------------------
// SymGetLinenumStmt
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumStmt(SYM_DESCRIPTOR       moduleDesc,
                  LINENUM_TYPE         linenum,
                  COLUMN_TYPE          column,
                  DESCRIPTOR           addrRangeDesc,
                  LINENUM_TYPE         *actualLinenum,
                  COLUMN_RANGE_TYPE    *columnRange,
                  LINENUM_DESCRIPTOR   *nextIndex)  {
   RETCODE err;
   
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;
   if ((err = SymGetSymbolAddress(moduleDesc, addrRangeDesc)) != GOOD)
      return err;
   return lt.GetLinenumStmt(moduleDesc,
                            linenum,
                            column,
                            addrRangeDesc,
                            actualLinenum,
                            columnRange,
                            nextIndex);
}  // end of SymGetLinenumStmt


//------------------------------------------------------------------------
// SymGetLinenumStmtByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumStmtByIndex(SYM_DESCRIPTOR     moduleDesc,
                         LINENUM_DESCRIPTOR index,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_TYPE       *linenum,
                         COLUMN_RANGE_TYPE  *columnRange,
                         LINENUM_DESCRIPTOR *nextIndex)  {
   RETCODE err;
   
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;
   if ((err = SymGetSymbolAddress(moduleDesc, addrRangeDesc)) != GOOD)
      return err;
   return lt.GetLinenumStmtByIndex(moduleDesc,
                                   index,
                                   addrRangeDesc,
                                   linenum,
                                   columnRange,
                                   nextIndex);
}  // end of SymGetLinenumStmtByIndex


//------------------------------------------------------------------------
// SymMapAddr2Linenum
//
// Design:
//    Convert abstract address into logical address
//       If sel:offset, offset is direct address to look up with linenum addr
//       If logical, must get base address pointed to by module desc base
//          index.  Subtract it from requested linenum address before search.
//    Check for line numbers loaded before calling the map function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2Linenum(DESCRIPTOR         linenumAddrDesc,
		   SYM_DESCRIPTOR     moduleDesc,
                   SYM_DESCRIPTOR     functionDesc,
                   LINENUM_TYPE       *linenum,
                   COLUMN_TYPE        *column,
                   DESCRIPTOR         addrRangeDesc,
                   LINENUM_DESCRIPTOR *index)  {

   RETCODE           err;
   OFFSET_ADDR_TYPE  offsetAddr;
   COLUMN_RANGE_TYPE columnRange;
   U16 segment;
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   // get offset address from the module descriptor and requested linenum
   // logical address
   //Hera 5/14/97
   if (functionDesc == 0)
      functionDesc = moduleDesc;

   if (GOOD != (err = UtilGetOffsetFromAddrDesc(functionDesc, //Hera 5/13/97
						linenumAddrDesc,
                                                &segment,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
			    segment,
                            offsetAddr,
                            moduleDesc,
                            linenum,
                            &columnRange,
                            addrRangeDesc,
                            index);
   *column = columnRange.columnStart;
   return err;
}  // end of SymMapAddr2Linenum


//------------------------------------------------------------------------
// SymMapAddr2LinenumStmt
//
// Design:
//    Convert abstract address into logical address
//       If sel:offset, offset is direct address to look up with linenum addr
//       If logical, must get base address pointed to by module desc base
//          index.  Subtract it from requested linenum address before search.
//    Check for line numbers loaded before calling the map function
//
// Error: returns ER_ADDRESS_NOT_FOUND if module desciptor not valid
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumStmt(DESCRIPTOR         linenumAddrDesc,
                       SYM_DESCRIPTOR     moduleDesc,
                       SYM_DESCRIPTOR     functionDesc, //Hera 5/12/97
                       LINENUM_TYPE       *linenum,
                       COLUMN_RANGE_TYPE  *columnRange,
                       DESCRIPTOR         addrRangeDesc,
                       LINENUM_DESCRIPTOR *index)  {

   RETCODE          err;
   OFFSET_ADDR_TYPE offsetAddr;
   U16 segment; 
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if(functionDesc == 0)
      functionDesc = moduleDesc;
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(functionDesc, //Hera
						linenumAddrDesc,
                                                &segment,
						&offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
			     segment,
                             offsetAddr,
                             moduleDesc,
                             linenum,
                             columnRange,
                             addrRangeDesc,
                             index);
}  // end of SymMapAddr2LinenumStmt


//------------------------------------------------------------------------
// SymMapAddr2LinenumModule
//
// Design:
//    Convert abstract address into logical address
//    Look up the module descriptor for the requested address
//    Check for line numbers loaded before calling the MapAddr2Linenum
//       function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumModule(DESCRIPTOR         linenumAddrDesc,
                         SYM_DESCRIPTOR     *moduleDesc,
                         DESCRIPTOR         modAddrRangeDesc,
                         LINENUM_TYPE       *linenum,
                         COLUMN_TYPE        *column,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   OFFSET_ADDR_TYPE offsetAddr;
   MEM_ADDR_CLASS memoryClass;
   COLUMN_RANGE_TYPE columnRange;
   U16 segment;

   FilterPublics = TRUE;  // global to skip over publics for lookup
   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   FilterPublics = FALSE;
   
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(*moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   COMMON_SYMBOL_HEADER  HUGE* modPtr;

   // set module address range
   if (funcDescriptor == 0)
      funcDescriptor = *moduleDesc;
   modPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(funcDescriptor);//Hera 5/15/97
   if ((err = UtilSetAddrParams(
                   modPtr->typeIndex.baseIndex,
                   modPtr->beginAddrInfo.startAddr,
                   modPtr->endAddrInfo.endAddr,
                   modAddrRangeDesc)) != GOOD)
      return err;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (funcDescriptor == 0)
      funcDescriptor = *moduleDesc;
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(funcDescriptor, //Hera 5/15/97
                                                linenumAddrDesc,
                                                &segment,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
			    segment,
                            offsetAddr,
                            *moduleDesc,
                            linenum,
                            &columnRange,
                            addrRangeDesc,
                            index);
   *column = columnRange.columnStart;
   return err;
}  // end of SymMapAddr2LinenumModule


//------------------------------------------------------------------------
// SymMapAddr2LinenumStmtModule
//
// Design:
//    Convert abstract address into logical address
//    Look up the module descriptor for the requested address
//    Check for line numbers loaded before calling the MapAddr2Linenum
//       function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumStmtModule(DESCRIPTOR         linenumAddrDesc,
                             SYM_DESCRIPTOR     *moduleDesc,
                             DESCRIPTOR         modAddrRangeDesc,
                             LINENUM_TYPE       *linenum,
                             COLUMN_RANGE_TYPE  *columnRange,
                             DESCRIPTOR         addrRangeDesc,
                             LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   OFFSET_ADDR_TYPE offsetAddr;
   MEM_ADDR_CLASS memoryClass;

   U16 segment;

   FilterPublics = TRUE;  // global to skip over publics for lookup
   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   FilterPublics = FALSE;
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(*moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   COMMON_SYMBOL_HEADER  HUGE* modPtr;

   // set module address range
  if (funcDescriptor == 0)
     funcDescriptor = *moduleDesc;
  modPtr = (COMMON_SYMBOL_HEADER HUGE*)st.GetHugeDataPtr(funcDescriptor);//Hera
  if ((err = UtilSetAddrParams(
                   modPtr->typeIndex.baseIndex,
                   modPtr->beginAddrInfo.startAddr,
                   modPtr->endAddrInfo.endAddr,
                   modAddrRangeDesc)) != GOOD)
      return err;
   // get offset address from the module descriptor and requested linenum
   // logical address
  if (GOOD != (err = UtilGetOffsetFromAddrDesc(funcDescriptor,
						linenumAddrDesc,
                                                &segment,
                                                &offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
                             segment,
                             offsetAddr,
                             *moduleDesc,
                             linenum,
                             columnRange,
                             addrRangeDesc,
                             index);
}  // end of SymMapAddr2LinenumStmtModule


//------------------------------------------------------------------------
// GetRawLinenumByIndex
//
// Purpose: return basic info on linenum; return next index.  If index
//          out of bounds, return error
//
// Error: returns ER_LINENUM_INDEX_TOO_LARGE when the input index is
//        beyond the end of the table; this is used to test for the end
//        of iteration
//------------------------------------------------------------------------
RETCODE EXPORT
GetRawLinenumByIndex(SYM_DESCRIPTOR     module,
                     LINENUM_DESCRIPTOR index,
                     DESCRIPTOR         linenumAddr,
                     LINENUM_TYPE       *linenum,
                     COLUMN_TYPE        *column,
                     LINENUM_DESCRIPTOR *nextIndex) {

   return lt.GetRawLinenumByIndex(module,
                                  index,
                                  linenumAddr,
                                  linenum,    // already a pointer
                                  column,     //    "
                                  nextIndex); //    "

}  // end of GetRawLinenumByIndex


//------------------------------------------------------------------------
// GetLinenumFirstIndex
//
// Purpose: Returns the index to the first linenum in module table
//
// Error: returns warning if there are no linenums              
//------------------------------------------------------------------------
RETCODE EXPORT
GetLinenumFirstIndex(SYM_DESCRIPTOR         moduleDesc,
                     LINENUM_DESCRIPTOR FAR *nextIndex) {

   return GetLinenumFirstIndex(moduleDesc,
                               nextIndex);  // already a pointer

}  // end of GetLinenumFirstIndex

//------------------------------------------------------------------------
// SymGetLastLinenum
//
// Design: Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLastLinenum(SYM_DESCRIPTOR  moduleDesc,
                  DESCRIPTOR      lastLinenumAddrRange,
                  LINENUM_TYPE    *lastLinenum) {
      
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_INVALID_SYM_DESCRIPTOR;
   return lt.GetLastLinenum(moduleDesc, lastLinenumAddrRange, lastLinenum);
}  // end of SymGetLastLinenum

#ifdef __cplusplus
}
#endif

//-----------------------------------------------------------------------
// LinenumCompare
//
// Purpose: 
//    Compares two linenum address values and returns < or >.  If =,
//    returns 0.
//
// Input params:
//    *first, *second: pointers to symbol offsets
//
// Output parameter:
//    return val: -1 -> first < second
//                 0 -> first = second
//                 1 -> first > second
//-----------------------------------------------------------------------
int LinenumCompare(const void _FAR *first,
                   const void _FAR *second) {

   
   register OFFSET_ADDR_TYPE first_val, second_val;
   register U16 first_seg, second_seg;

   // make passed parameters point to linenum structures
   first_seg = ((LINENUM_STRUCT_TYPE *) first)->segment;
   first_val  = ((LINENUM_STRUCT_TYPE *)first)->addr;
   second_seg = ((LINENUM_STRUCT_TYPE *)second)->segment;
   second_val = ((LINENUM_STRUCT_TYPE *)second)->addr;

   if (first_seg == second_seg && first_val < second_val) {
      return -1;
   } else if (first_seg == second_seg && first_val > second_val) {
      return 1;
   } else {
      return 0;
   }
}  // end of LinenumCompare
//Hera 6/17/97
int LinenumCompare1(const void _FAR *first,
                   const void _FAR *second) {

   
   register OFFSET_ADDR_TYPE first_val, second_val;
   // make passed parameters point to linenum structures
   first_val  = ((LINENUM_STRUCT_TYPE *)first)->linenum;
   second_val = ((LINENUM_STRUCT_TYPE *)second)->linenum;

   if (first_val < second_val) {
      return -1;
   } else if (first_val > second_val) {
      return 1;
   } else {
      return 0;
   }
}  // end of LinenumCompare1


//------------------------------------------------------------------------
// UpdateLinenumEndAddr
//
// Purpose: Updates the dummy address to the value passed in.  This is
//          called when the module range is updated from SymAddSymbolSetAddr
//
// Input parameters:
//    modulePtr: pointer to module object
//    endAddr: address to fill in to last (dummy) linenum
// 
// Error:
//------------------------------------------------------------------------
RETCODE
UpdateLinenumEndAddr(SYM_TYPE_MODULE HUGE *modulePtr,
                     U32                  endAddr) {

   TABLE_OFFSET        offset;
   LINENUM_STRUCT_TYPE HUGE* linenumPtr;
   BaseSymbol          HUGE* basePtr;
   TABLE_OFFSET        tmpBaseOffset;
   RETCODE             err;

   // return if no line numbers have been added
   if (modulePtr->linenumOffsetEnd <= modulePtr->linenumOffsetStart)
      return GOOD;

   // calculate pointer to last (dummy) linenum
   offset = (modulePtr->linenumOffsetEnd);
   linenumPtr = (LINENUM_STRUCT_TYPE HUGE*)lt.GetHugeDataPtr(offset);

   // save the endAddr if <= value already saved, otherwise save it and
   // increment it if not a max offset address
   if (linenumPtr->addr <= endAddr) {
      linenumPtr->addr = endAddr;
      // now increment if not already at max value
      // see comments in LinenumTable::LinenumEnd() function
      if((err =
          bit.GetOffset(modulePtr->symHeader.symHeader.typeIndex.baseIndex,
                        tmpBaseOffset)) != GOOD)
         return(err);
      // get pointer to BaseSymbol, then check for real mode
      basePtr = (BaseSymbol HUGE*)st.GetHugeDataPtr(tmpBaseOffset);
      if (((SYM_REAL == (basePtr->GetSymMode())) &&    // real-mode segment
           ((linenumPtr->addr) < 0xFFFFL)) ||
          ((linenumPtr->addr) < 0xFFFFFFFFL)) {
         linenumPtr->addr++;
      }
   }
   return GOOD;
   
}  // end of UpdateLinenumEndAddr

/******************************** E O F *************************************/
