/*----------------------------------------------------------------------------
** Name: linenum.cpp
**
** Title: Line Number Table
**--------------------------------------------------------------------------*/

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

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

#ifndef __MEM_H
#include <mem.h>
#endif

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

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

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

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

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

#ifndef _LDRSVR_
#include "ldrsvr.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              0xFFFFFFFFL
#define MAX_LINENUM            0xFFFFFFFFL


LinenumTable lt;       // declare static line number table object

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern MemPool st;


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

//Hera 1/5/97
int LinenumCompare1(const void _FAR *first,const void _FAR *second);
int LinenumCompare2(const void _FAR *first,const void _FAR *second);

//------------------------------------------------------------------------
//                  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;
};  // 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;

} // 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 1/5/97
   /*if (lineStruct.addr < previousLine.addr)  {
      sorted = FALSE;
   }*/
   if (lineStruct.linenum < previousLine.linenum){
      sorted = FALSE;
   }
   //Hera 1/8/98
   if(previousLine.addr != 0) {
      LINENUM_STRUCT_TYPE *lineptr1, *lineptr2;
      TABLE_OFFSET offset;
      U32 offsetstart,offsetend;
      offsetstart = linenumOffsetStart;
      offsetend = GetAvailMemOffset();
      lineptr1 = (LINENUM_STRUCT_TYPE *) GetHugeDataPtr(previousLinenumOffset);
      for (offset = offsetstart; offset < offsetend;
                                        offset += LINENUM_STRUCT_SIZE)  {
	 lineptr2 = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
	 //Is it a FOR LOOP?
	 // codeaddr1  32
	 // codeaddr2  33
	 // codeaddr3  32
         // codeaddr4  34
         // merge codeaddr2 ~codeaddr4 for line33
	 if (lineptr2->linenum == lineStruct.linenum)
	    return (GOOD);      
      }          
      lineptr1->oplen = (S16)(lineStruct.addr - previousLine.addr); 
   }
   /*if (previousLine.addr != 0) {
      LINENUM_STRUCT_TYPE *lineptr1;
      lineptr1 = (LINENUM_STRUCT_TYPE *) GetHugeDataPtr(previousLinenumOffset);
      lineptr1->oplen = (S16)(lineStruct.addr-previousLine.addr);
   }*/
   //Hera
   // 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
   if (previousLine.addr == lineStruct.addr) {
      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
   previousLine.linenum = lineStruct.linenum;
   linenums++;  // keep a count
   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;
   S16 oplen; //Hera 1/8/98

   // 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;
   U32          linenumDelta;    // difference between requested linenum and
                                 // comparison linenum
   U32          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;
   oplen = linenumPtr->oplen; //Hera 1/8/98
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =
      UtilSetAddrOffset(module, 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 *)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;
   //Hera 1/8/98
   if (oplen > 0) {
      endAddr = startAddr+(U16)oplen-1;
   }else if (endAddr > startAddr) {
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }//Hera
   /*if (endAddr > startAddr ) { // valid end address
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }*/
   return UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);
}  // 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,
                                DESCRIPTOR         addrRangeDesc,
                                LINENUM_TYPE       *actualLinenum,
                                LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE         err;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   S16 oplen; //Hera 1/8/98

   // 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;
   oplen = linenumPtr->oplen;//Hera 1/8/98
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =  UtilSetAddrOffset(module, 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 *)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;
   //Hera 1/8/98
   if (oplen > 0) {
      endAddr = startAddr+(U16)oplen-1;
   }else if (endAddr > startAddr) {
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }//Hera
   /*if (endAddr > startAddr) { // valid end address
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }*/
   return UtilSetEndAddrOffset(module, 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
   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;

   startAddr = linenumPtr->addr;   // return the start address for linenum
   return UtilSetAddrOffset(module, 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;
   S16 oplen;//Hera 1/8/98

   // 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);

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

   // 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;
   //Hera 1/8/98
   if (oplen > 0) {
      endAddr = startAddr+(U16)oplen-1;
   }else if (endAddr > startAddr) {
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }//Hera
   /*Hera 1/8/98
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }else {
      endAddr--;
   }*/
   return UtilSetEndAddrOffset(module, 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 *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,
                             DESCRIPTOR         addrRangeDesc,
                             LINENUM_TYPE       *actualLinenum,
                             COLUMN_RANGE_TYPE  *actualColumnRange,
                             LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE  err;
   OFFSET_ADDR_TYPE startAddr, endAddr;

   TABLE_OFFSET offset;
   S16 oplen; //Hera 1/8/98
   // 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
   U32         linenumDelta = MAX_DELTA;
   U32         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
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   oplen = linenumPtr->oplen; //Hera 1/8/98
   if (GOOD != (err =
      UtilSetAddrOffset(module, 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 *)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;
   //Hera 1/8/98
   if (oplen > 0) {
      endAddr = startAddr+(U16)oplen-1;
   }else if (endAddr > startAddr) {
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }//Hera
   /*if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }*/
   return UtilSetEndAddrOffset(module, 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;
   S16 oplen; //Hera 1/8/98
   // 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

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   oplen = linenumPtr->oplen; //Hera 1/8/98
   if (GOOD != (err =
      UtilSetAddrOffset(module, 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 *)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;
   //Hera 1/8/98
   if (oplen > 0) {
      endAddr = startAddr+(U16)oplen-1;
   }else if (endAddr > startAddr) {
      endAddr--;
   }else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }//Hera
   /*Hera 1/8/98
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      firstErr = ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }*/
   err = UtilSetEndAddrOffset(module, 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 *)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;
   LINENUM_STRUCT_TYPE *linenumPtr;
   SYM_TYPE_MODULE     *modulePtr;
   LINENUM_STRUCT_TYPE lastLinenumVar;
   U32                 linenumAddr;
   RETCODE             err;

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

   // test to see if any line numbers have been added
   if (linenumOffsetEnd <= linenumOffsetStart) {
      linenumLoadState = NOT_LOADING;  // <Judy 5/10/96>
      return ER_NO_LINENUMS_ADDED;
   }

   // get offset to last added line number, then address of that linenum
   offset = linenumOffsetEnd - LINENUM_STRUCT_SIZE;
   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
   linenumAddr = linenumPtr->addr;

   // 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 + 1.  The value must be one greater than the range
   // of the module so the binary search will see that the requested address
   // is inside the last linenum and this "made-up" linenum.

   lastLinenumVar.addr = modulePtr->symHeader.symHeader.endAddrInfo.endAddr;
   if (lastLinenumVar.addr < 0xFFFFFFFFL)
      lastLinenumVar.addr++;

   // compare module end address with last line number address saved;
   // if module address is smaller, set the created last line number address
   // to one more than previous line number
   if (lastLinenumVar.addr <= linenumAddr)  {
      lastLinenumVar.addr = linenumAddr + 1L;
   }
   lastLinenumVar.linenum = MAX_LINENUM;
   lastLinenumVar.column = SYM_MAX_COLUMN;
   if ((err = AddLinenum(lastLinenumVar)) != GOOD) return err;

   linenumLoadState = NOT_LOADING;

   if (!sorted)  {
      // sort list
      //Hera 1/5/97
      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);
      //return ER_LINENUMS_NOT_SORTED;
   }       
   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);
   //Hera 2/3/98
   if (modulePtr->linenumOffsetEnd)  {
      // non-zero 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,
                              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 *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;
   //Hera 1/9/97 sorted by address
   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
	    LinenumCompare2);
   //Hera
   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) {
      err = GetLinenumByIndex(module,
                               (middle * LINENUM_STRUCT_SIZE),
                                     // offset to found linenum
                               addrRangeDesc,
                               index,
                               linenum,
			       &(columnRange->columnStart));     
   } else { //
      err = GetLinenumStmtByIndex(module,
                                   (middle * LINENUM_STRUCT_SIZE),
                                      // offset to found linenum
                                   addrRangeDesc,
                                   linenum,
                                   columnRange,
                                   index);
   }
   //Hera 1/9/97 sorted by linenum
   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);
    return(err);
   //Hera

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


#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 EXPORT
SymAddLinenum(LINENUM_TYPE     linenum,
              U16              column,
              OFFSET_ADDR_TYPE addr)  {

   LINENUM_STRUCT_TYPE  linenumVar;

   memset(&linenumVar,'\0',sizeof(LINENUM_STRUCT_TYPE));
   linenumVar.addr = addr;
   linenumVar.linenum = linenum;
   linenumVar.column = column;
   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)  {

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

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

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

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

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

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

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

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

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

   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,
                   LINENUM_TYPE       *linenum,
                   COLUMN_TYPE        *column,
                   DESCRIPTOR         addrRangeDesc,
                   LINENUM_DESCRIPTOR *index)  {

   RETCODE           err;
   OFFSET_ADDR_TYPE  offsetAddr;
   COLUMN_RANGE_TYPE columnRange;

   /* 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 (GOOD != (err = UtilGetOffsetFromAddrDesc(moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
                            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,
                       LINENUM_TYPE       *linenum,
                       COLUMN_RANGE_TYPE  *columnRange,
                       DESCRIPTOR         addrRangeDesc,
                       LINENUM_DESCRIPTOR *index)  {

   RETCODE          err;
   OFFSET_ADDR_TYPE offsetAddr;

   /* 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 (GOOD != (err = UtilGetOffsetFromAddrDesc(moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
                             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;

   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   /* 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);
   if (GOOD != (err =
      UtilSetAddrOffset(*moduleDesc,
                        modPtr->beginAddrInfo.startAddr,
                        modAddrRangeDesc)))
      return err;
   if (GOOD != (err =
      UtilSetEndAddrOffset(*moduleDesc,
                           modPtr->endAddrInfo.endAddr,
                           modAddrRangeDesc)))
      return err;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(*moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
                            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;

   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   /* 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);
   if (GOOD != (err =
      UtilSetAddrOffset(*moduleDesc,
                        modPtr->beginAddrInfo.startAddr,
                        modAddrRangeDesc)))
      return err;
   if (GOOD != (err =
      UtilSetEndAddrOffset(*moduleDesc,
                           modPtr->endAddrInfo.endAddr,
                           modAddrRangeDesc)))
      return err;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(*moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
                             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

#ifdef __cplusplus
}
#endif
//Hera 1/5/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
//Hera 1/5/97
int LinenumCompare2(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 LinenumCompare2
/******************************** E O F *************************************/
