
/***************************************************************************
**
**    $Header:   D:/EP196/SRC/LOG/SYMSVR/SYMLINE.CPP   1.0   15 Aug 1997 14:04:16   ZJRD  $
**
**    $Log:   D:/EP196/SRC/LOG/SYMSVR/SYMLINE.CPP  $
** 
**    Rev 1.0   15 Aug 1997 14:04:16   ZJRD
** Initial revision.
** 
****************************************************************************/

/*----------------------------------------------------------------------------
** 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: PRELIMINARY
**
** $Log$
** 
** $Header:   D:/EP196/SRC/LOG/SYMSVR/SYMLINE.CPP   1.0   15 Aug 1997 14:04:16   ZJRD  $
**
** Copyright (C) 1995 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

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

#include "stdafx.h"                      
#include <stdlib.h>
#include "symbase.h"
#include "symline.h"
#include "sympool.h"
#include "symcli.h"
#include "symget.h"

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

LinenumTable lt;       // declare static line number table object

                       /****************************
                        *                          *
                        *    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
                              // publics or not


                       /****************************
                        *                          *
                        *     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;
   modulesLoaded = 0L;
   previousLinenumOffset = 0L;  // start offset to add line numbers
};  // 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
} // 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
   if (lineStruct.addr < previousLine.addr)  {
      sorted = FALSE;
   }
   // 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;
         return GOOD;
      }
   }
   // save state for next compare
   previousLine.addr = lineStruct.addr;  // save for next compare

   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,
                         ADDR_RANGE_TYPE    *addrRange,
                         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;
   LINENUM_STRUCT_TYPE *linenumPtr;

   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 *)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;
         }
      }
      else if ((linenumPtr->linenum) == requestedLinenum)  {
            closestOffset = offset;
            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 *)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;
   *actualColumn  = linenumPtr->column;
   addrRange->startAddr = startAddr = linenumPtr->addr;
   *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 *)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
          ((linenumPtr->addr) > startAddr))
         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;
   if (endAddr > startAddr) { // valid end address
      endAddr--;
   } else {
      // return a valid address range as well as an error
      addrRange->endAddr = startAddr;
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   addrRange->endAddr = endAddr;
   return GOOD;

}  // end of LinenumTable::GetLinenum

//------------------------------------------------------------------------
// 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,
                                ADDR_RANGE_TYPE    *addrRange,
                                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 *linenumPtr;

   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 *)GetHugeDataPtr(offset);

      if ((linenumPtr->linenum) >= requestedLinenum)  {
         closestOffset = offset;
         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 *)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;

   // fill in start address of line number found
   addrRange->startAddr = startAddr = linenumPtr->addr;

   *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 *)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
          ((linenumPtr->addr) > startAddr))
         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;
   if (endAddr > startAddr) { // valid end address
      endAddr--;
   } else {
      // return a valid address range as well as an error
      addrRange->endAddr = startAddr;
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   addrRange->endAddr = endAddr;
   return GOOD;
}  // 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,
                                   OFFSET_ADDR_TYPE   *linenumAddr,
                                   LINENUM_TYPE       *linenum,
                                   COLUMN_TYPE        *column,
                                   LINENUM_DESCRIPTOR *nextIndex) {

   RETCODE err;

   // 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 *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   *nextIndex = index + LINENUM_STRUCT_SIZE;
   *linenumAddr = linenumPtr->addr;   // return the start address for linenum
   return GOOD;
}  // 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 addrRange
//       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,
                                ADDR_RANGE_TYPE    *addrRange,
                                LINENUM_DESCRIPTOR *nextIndex,
                                LINENUM_TYPE       *linenum,
                                COLUMN_TYPE        *column)  {

   RETCODE err;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   LINENUM_DESCRIPTOR testIndex;
   LINENUM_DESCRIPTOR tempIndex;
   tempIndex = index;
   // 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 *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   *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 *)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 *)GetHugeDataPtr(index);
   addrRange->startAddr = startAddr = linenumPtr->addr;

   // search forward for end of contiguous linenums
   index = *nextIndex;  // recover to one past retrieved linenum
   do {
      linenumPtr = (LINENUM_STRUCT_TYPE *)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 *)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;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      addrRange->endAddr = startAddr;
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   addrRange->endAddr = endAddr;   
   if(tempIndex == *nextIndex) *nextIndex=0;
   return GOOD;
}  // 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 *linenumPtr;
   SYM_TYPE_MODULE     *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDesc);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      return ER_NO_LINENUMS_ADDED;  // interpreted as no linenums in table
   }
   linenumPtr = 
      (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(modulePtr->linenumOffsetStart);
   firstLinenum = linenumPtr->linenum;

   // last valid linenum is the one previous to the last in table
   linenumPtr = (LINENUM_STRUCT_TYPE *)
      GetHugeDataPtr(modulePtr->linenumOffsetEnd-LINENUM_STRUCT_SIZE);
   lastLinenum = 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 *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)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,
                             ADDR_RANGE_TYPE    *addrRange,
                             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 *linenumPtr;
   // difference between requested linenum and comparison linenum
   U16         linenumDelta = MAX_DELTA;
   U16         tmpDelta;
   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 *)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 *)GetHugeDataPtr(closestOffset);

   *actualLinenum = linenumPtr->linenum;

   // fill in start address of line number found
   addrRange->startAddr = startAddr = linenumPtr->addr;

   // 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 *)GetHugeDataPtr(offset);
      if ((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;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      addrRange->endAddr = startAddr;
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   addrRange->endAddr = endAddr;
   return GOOD;
}  // 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 addrRange
//    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,
                                    ADDR_RANGE_TYPE    *addrRange,
                                    LINENUM_TYPE       *linenum,
                                    COLUMN_RANGE_TYPE  *columnRange,
                                    LINENUM_DESCRIPTOR *nextIndex)  {

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

   // 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 *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   columnRange->columnEnd  = linenumPtr->column;  // marks the end point
   addrRange->startAddr = startAddr = linenumPtr->addr;
   *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 *)GetHugeDataPtr(offset);
      if ((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;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      addrRange->endAddr = startAddr;
      firstErr = ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   addrRange->endAddr = endAddr;

   // 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 *)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 *lastLinenumPtr, *firstLinenumPtr;
   SYM_TYPE_MODULE     *modulePtr;
   LINENUM_STRUCT_TYPE lastLinenumVar;
   RETCODE             err;
   BaseSymbol          *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 

// Changed by Gates Hua , Because of one module only have one line 
//              information , It will have bug .
//   if (previousLinenumOffset <= linenumOffsetStart) {
   if (previousLinenumOffset < linenumOffsetStart) {

      // store linenum pointers into module structure
      modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(cachedModDescriptor);
      modulePtr->linenumOffsetStart = linenumOffsetStart;
      modulePtr->linenumOffsetEnd = linenumOffsetEnd;
      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.
   if (!sorted)  { // sort list
#ifdef DEBUG      
      S8 str[40];
      sprintf(str, "Sorting %d line numbers...",
       (U16)(((linenumOffsetEnd-linenumOffsetStart)/LINENUM_STRUCT_SIZE + 1)));
      SendMessageToCli((LPU8)str);
#endif      
      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
            LinenumCompare
            );
      // 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 *)st.GetHugeDataPtr(cachedModDescriptor);
   modulePtr->linenumOffsetStart = linenumOffsetStart;
   modulePtr->linenumOffsetEnd = linenumOffsetEnd;

   firstLinenumPtr = (LINENUM_STRUCT_TYPE *)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 *)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.  
      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 *)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;
   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 *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)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;
   previousLine.addr = 0L;
   previousLine.linenum = 0;
   previousLine.column = 0;
   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,
                              OFFSET_ADDR_TYPE   requestedAddr,
                              SYM_DESCRIPTOR     module,
                              LINENUM_TYPE       *linenum,
                              COLUMN_RANGE_TYPE  *columnRange,
                              ADDR_RANGE_TYPE    *addrRange,
                              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 *linenumPtr, *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;

   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
                               addrRange,
                               index,
                               linenum,
                               &(columnRange->columnStart));
   } else { //
      return GetLinenumStmtByIndex(module,
                                   (middle * LINENUM_STRUCT_SIZE),
                                      // offset to found linenum
                                   addrRange,
                                   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 *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 *)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 *)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 *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)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 WINAPI
SymAddLinenum(LINENUM_TYPE     linenum,
              U8               column,
              OFFSET_ADDR_TYPE addr)  {

   LINENUM_STRUCT_TYPE  linenumVar;

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


//------------------------------------------------------------------------
// SymAddLinenumEnd
//------------------------------------------------------------------------
RETCODE WINAPI
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 WINAPI
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 WINAPI
SymGetLinenum(SYM_DESCRIPTOR     moduleDesc,
              LINENUM_TYPE       linenum,
              ADDR_RANGE_TYPE    *addrRange,
              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, addrRange)) != GOOD)
      return err;
   return (lt.GetLinenum(moduleDesc,
                         linenum,
                         addrRange,
                         actualLinenum,
                         actualColumn,
                         nextIndex));
}  // end of SymGetLinenum

//------------------------------------------------------------------------
// SymGetLinenumInOrder
//
// Design:  Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE WINAPI
SymGetLinenumInOrder(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_TYPE       linenum,
                     ADDR_RANGE_TYPE    *addrRange,
                     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, addrRange)) != GOOD)
      return err;

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

//------------------------------------------------------------------------
// SymGetLinenumByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE WINAPI
SymGetLinenumByIndex(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_DESCRIPTOR index,
                     ADDR_RANGE_TYPE    *addrRange,
                     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, addrRange)) != GOOD)
      return err;
   return lt.GetLinenumByIndex(moduleDesc,
                               index,
                               addrRange,
                               nextIndex,
                               linenum,
                               column);
}  // end of SymGetLinenumByIndex


//------------------------------------------------------------------------
// SymGetLinenumStmt
//
// Design:
//------------------------------------------------------------------------
RETCODE WINAPI
SymGetLinenumStmt(SYM_DESCRIPTOR       moduleDesc,
                  LINENUM_TYPE         linenum,
                  COLUMN_TYPE          column,
                  ADDR_RANGE_TYPE      *addrRange,
                  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, addrRange)) != GOOD)
      return err;
   return lt.GetLinenumStmt(moduleDesc,
                            linenum,
                            column,
                            addrRange,
                            actualLinenum,
                            columnRange,
                            nextIndex);
}  // end of SymGetLinenumStmt


//------------------------------------------------------------------------
// SymGetLinenumStmtByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE WINAPI
SymGetLinenumStmtByIndex(SYM_DESCRIPTOR     moduleDesc,
                         LINENUM_DESCRIPTOR index,
                         ADDR_RANGE_TYPE    *addrRange,
                         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, addrRange)) != GOOD)
      return err;
   return lt.GetLinenumStmtByIndex(moduleDesc,
                                   index,
                                   addrRange,
                                   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 WINAPI
SymMapAddr2Linenum(OFFSET_ADDR_TYPE   linenumAddr,
                   SYM_DESCRIPTOR     moduleDesc,
                   LINENUM_TYPE       *linenum,
                   COLUMN_TYPE        *column,
                   ADDR_RANGE_TYPE    *addrRange,
                   LINENUM_DESCRIPTOR *index)  {

   RETCODE           err;
   COLUMN_RANGE_TYPE columnRange;

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   err = lt.MapAddr2Linenum(SYM_LINE,
                            linenumAddr,
                            moduleDesc,
                            linenum,
                            &columnRange,
                            addrRange,
                            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 WINAPI
SymMapAddr2LinenumStmt(OFFSET_ADDR_TYPE   linenumAddr,
                       SYM_DESCRIPTOR     moduleDesc,
                       LINENUM_TYPE       *linenum,
                       COLUMN_RANGE_TYPE  *columnRange,
                       ADDR_RANGE_TYPE    *addrRange,
                       LINENUM_DESCRIPTOR *index)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return lt.MapAddr2Linenum(SYM_STMT,
                             linenumAddr,
                             moduleDesc,
                             linenum,
                             columnRange,
                             addrRange,
                             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 WINAPI
SymMapAddr2LinenumModule(OFFSET_ADDR_TYPE   linenumAddr,
                         SYM_DESCRIPTOR     *moduleDesc,
                         ADDR_RANGE_TYPE    *modAddrRange,
                         LINENUM_TYPE       *linenum,
                         COLUMN_TYPE        *column,
                         ADDR_RANGE_TYPE    *addrRange,
                         LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   MEM_ADDR_CLASS memoryClass;
   COLUMN_RANGE_TYPE columnRange;
   ADDRESS_TYPE lineAddr;

   FilterPublics = TRUE;  // global to skip over publics for lookup
   lineAddr.segType = SEG_CODE;
   lineAddr.addr = linenumAddr;
   if ((err = SymMapAddr2Symbol(lineAddr,
                                &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  *modPtr;

   // set module address range
   modPtr = (COMMON_SYMBOL_HEADER *)st.GetHugeDataPtr(*moduleDesc);
   modAddrRange->segType   = (SEGMENTTYPE)modPtr->typeIndex.baseIndex;
   modAddrRange->startAddr = modPtr->beginAddrInfo.startAddr;
   modAddrRange->endAddr   = modPtr->endAddrInfo.endAddr;

   err = lt.MapAddr2Linenum(SYM_LINE,
                            linenumAddr,
                            *moduleDesc,
                            linenum,
                            &columnRange,
                            addrRange,
                            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 WINAPI
SymMapAddr2LinenumStmtModule(OFFSET_ADDR_TYPE   linenumAddr,
                             SYM_DESCRIPTOR     *moduleDesc,
                             ADDR_RANGE_TYPE    *modAddrRange,
                             LINENUM_TYPE       *linenum,
                             COLUMN_RANGE_TYPE  *columnRange,
                             ADDR_RANGE_TYPE    *addrRange,
                             LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   MEM_ADDR_CLASS memoryClass;
   ADDRESS_TYPE lineAddr;

   FilterPublics = TRUE;  // global to skip over publics for lookup
   lineAddr.segType = SEG_CODE;
   lineAddr.addr = linenumAddr;
   if ((err = SymMapAddr2Symbol(lineAddr,
                                &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  *modPtr;

   // set module address range
   modPtr = (COMMON_SYMBOL_HEADER *)st.GetHugeDataPtr(*moduleDesc);
   modAddrRange->segType   = (SEGMENTTYPE)modPtr->typeIndex.baseIndex;
   modAddrRange->startAddr = modPtr->beginAddrInfo.startAddr;
   modAddrRange->endAddr   = modPtr->endAddrInfo.endAddr;

   return lt.MapAddr2Linenum(SYM_STMT,
                             linenumAddr,
                             *moduleDesc,
                             linenum,
                             columnRange,
                             addrRange,
                             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 WINAPI
GetRawLinenumByIndex(SYM_DESCRIPTOR     module,
                     LINENUM_DESCRIPTOR index,
                     OFFSET_ADDR_TYPE   *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 WINAPI
SymGetLinenumFirstIndex(SYM_DESCRIPTOR         moduleDesc,
                     LINENUM_DESCRIPTOR FAR *nextIndex) {

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

}  // end of SymGetLinenumFirstIndex

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

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

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

//------------------------------------------------------------------------
// 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 *linenumPtr;
   BaseSymbol          *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 *)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 *)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 *************************************/
