/* CLASS: SymbolLib
   Provide interface to Symbol Server DLL.

*/!!

inherit(ProvidedLibs, #SymbolLib, nil, 2, nil)!!

now(class(SymbolLib))!!

/* semiPRIVATE -- should only be called via require(SymbolLib)
   Define entry points and load library.  Define any structs required.
*/
Def provide(self, path)
{
  ^initialise( new(self:ancestor), path )
}!!

now(SymbolLib)!!

/* 2/5/1998 18:25 */
Def getAliasModuleName(self, moduleN | stringRef)
{
  stringRef := new( Struct, 256 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETMODULEALIAS], moduleN, stringRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^removeNulls(physicalString(stringRef));
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}!!

/* 03/23/94 - PUBLIC
   Retrieve the all the modules descriptor in the symbol table.
   If successful, return #(numSymbols, OrderedCollectionOfSymbols) 
   else return nil.
*/
Def getAllModules(self, numSymbols | modDescListRef, numRetRef, list, numReturned, endOffset)
{
  if (numSymbols = 0) then
    ^nil;
  endif;
  /* Allocates variables */
  modDescListRef := new(Struct, (4 * numSymbols));
  numRetRef   := new(Struct, 4);

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETALLMODULES], modDescListRef, numSymbols, numRetRef);
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;
  endif;  

  if ((numReturned := wordAt(numRetRef, 0)) = 0)
    ^nil; /* nothing returned */
  endif;
  /* Create a new OrderedCollection */
  list := new(OrderedCollection, numReturned);
  endOffset := numReturned * 4;
  do (overBy(0, endOffset, 4),
    {using(offset)
      add(list, longAt(modDescListRef, offset));
    });   
            
  /* return: #(numReturned, OrderedCollection(...)) */          
  ^tuple(numReturned, list);
}
!!

/* 03/23/94 - PUBLIC
  Return the symbol statistics in the tuple #(numSymbols, numModules, numTypes).
  if error or no symbol loaded return nil.
*/
Def getSymbolsInfo(self | stringRef, numSymRef, numModRef, numTypesRef)
{
  stringRef   := new(Struct, 128);
  numSymRef   := new(Struct, 4);
  numModRef   := new(Struct, 4);
  numTypesRef := new(Struct, 4);

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETLDRSTATS],
                      stringRef,      /* LPSTR */
                      numSymRef,      /* *U32  */
                      numModRef,      /* *U16  */
                      numTypesRef) ;  /* *U32  */ 
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;
  endif;
  
  /* RETURN #(numSymbol, numModule, numTypes) */
  ^tuple(longAt(numSymRef, 0),
         wordAt(numModRef, 0 ),
         longAt(numTypesRef, 0));
}
!!

/* 7/9/1993 16:23 -PUBLIC
   Map an address to its containing module. 
   Returns nil or the <Module Descriptor>.
*/
Def addr2Module(self, intialAddrDesc | moduleDRef, memtypeRef
  symtypeRef, offsetRef, funcDRef, parentDRef)
{
  memtypeRef  := new(Struct, 4);
  symtypeRef  := new(Struct, 4);
  offsetRef   := new(Struct, 4); /* closest symbol descriptor */
  moduleDRef  := new(Struct, 4);
  funcDRef    := new(Struct, 4);  
  parentDRef  := new(Struct, 4);

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMMAPADDR2SYMBOL],
                      intialAddrDesc,  /* address descriptor for module */
                      memtypeRef,      /* mem class hint */
                      symtypeRef,      /* symbol type class */
                      offsetRef,       /* offset from start address */
                      moduleDRef,      /* module descriptor */
                      funcDRef,        /* function descriptor (not used) */
                      parentDRef       /* parent module descriptor */
                    ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    if (lastError <> ER_ADDRESS_NOT_FOUND) then
      displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP); 
    endif;  
    ^nil;
  endif;
  
  /* if not type SYM_MODULE or NULL_SYMBOL return nil */
  if ((wordAt(symtypeRef, 0) <> SYM_MODULE) cor 
     (longAt(moduleDRef, 0) = 0L)) then  
    ^nil;
  endif;  
  /* Return moduleDesc  */
  ^longAt(moduleDRef, 0);
 }
!!

/* 4/2/1992 0:42 - PUBLIC
  Given a moduleDesc and requested line number, get its actual line number
  information.
  return #(addrDesc, lineNum, lineNumDesc) or nil
  Note: Caller must destroy addrDesc when done.
*/
Def getLineNumInfoOrder(self, moduleDesc, lineNum 
| addrDesc, lineNumRef, nextIdxRef )
{ 
  /* Create addrDesc */
  if not(addrDesc := createAddress(AddressLibClass$Inst)) then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError( AddressLibClass$Inst ) ;
    ^nil;
  endif ;
  lineNumRef := new(Struct, 4);
  nextIdxRef := new(Struct, 4);
  
  if pcallLock(self) then
    lastError := pcall(procs[#SYMGETLINENUMINORDER], 
    asLong(moduleDesc),     /* U32 */
    lineNum,                /* U16 */
    addrDesc,
    lineNumRef,
    nextIdxRef);
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    if ((lastError <> ER_LINENUM_INDEX_TOO_LARGE) cand
      (lastError <> ER_LINENUM_NOT_FOUND)) then
      displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    endif;
    destroyAddress( AddressLibClass$Inst, addrDesc);
    ^nil;               
  endif;
  /* RETURN #(addrDesc, lineNum,lineNumDesc) */  
  ^tuple(addrDesc, 
       wordAt(lineNumRef, 0), 
       longAt(nextIdxRef, 0));     
}
!!

/* PUBLIC -- returns #( symbolDesciptor, type, isGlobal? ) on success
   Where `type' is one of #functionName, #variableName, or #other.
*/
Def primeSymbolFromContext(self, blockDescriptor, name
                    | typeRef, symDescRef, symTypeType, boolRef)
{
  typeRef    := new( Struct, 4 ) ;
  putWord( typeRef, SYM_UNDEFINED, 0 ) ;
  symDescRef := new( Struct, 4 ) ;
  boolRef    := new( Struct, 4 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETSYMBOLFROMCONTEXT],
                      blockDescriptor,
                      asciiz(name), typeRef, symDescRef, boolRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError = GOOD) then
    symTypeType := wordAt(typeRef, 0) ;
    select
      case (symTypeType = 2) /* SYM_FUNCTION */
      is symTypeType := #functionName ;
      endCase
      case (symTypeType = 4) or (symTypeType = 5) /* SYM_GLOBAL_VAR or SYM_LOCAL_VAR */
      is symTypeType := #variableName ;
      endCase
      case (symTypeType = 12)
      is symTypeType := #publicLabel ;
      endCase
      default 
        symTypeType := #other ;
    endSelect;
    ^tuple( longAt(symDescRef, 0), symTypeType, (byteAt(boolRef, 0) <> 0) )
  endif;
  ^nil;
}
!!

/* PUBLIC -- returns blockDesciptor on success
  Notes: This is a prime function, do not display error popup.
  To display error, after calling the method, get the lastError
  of the SymbolLib and do a displayError() with the lastError.
*/
Def primeLine2blockDescriptor(self, moduleDescriptor, lineNum
                        | typeRef, symDescRef)
{
  typeRef    := new( Struct, 4 ) ;
  symDescRef := new( Struct, 4 ) ;
  if pcallLock(self) then
    lastError := pcall( procs[#SYMMAPLINENUM2FUNCORBLOCK],
                      moduleDescriptor, lineNum, typeRef, symDescRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^longAt(symDescRef, 0);
  endif;
  ^nil;
}
!!

/* 7/14/1992 10:57 - PUBLIC
  Given a module descriptor and a valid index into the line number table,
  retrieve the line number information of the lineNum index, and return
  the index to the next address range. 
    Return #(addrDesc, nextIndxDesc, actualLineNum, actualColNum)
*/
Def getLineNumByIndex(self, moduleDesc, indexDesc | addrDesc, nextIndxRef, 
  lineNumRef, colNumRef)
{ 
  /* Create addrDesc */
  if not(addrDesc := createAddress(AddressLibClass$Inst)) then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError(AddressLibClass$Inst);
    ^nil;
  endif ;
  lineNumRef := new(Struct, 4);
  colNumRef  := new(Struct, 4);
  nextIndxRef := new(Struct, 4);
  
  if pcallLock(self) then
    lastError := pcall(procs[#SYMGETLINENUMBYINDEX], 
    moduleDesc,     /* U32 */
    indexDesc,      /* U32 */
    addrDesc,
    nextIndxRef,
    lineNumRef,
    colNumRef 
    );
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    if ((lastError <> ER_LINENUM_INDEX_TOO_LARGE) cand
      (lastError <> ER_LINENUM_NOT_FOUND)) then
      displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    endif;
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil;               
  endif;
  /* Return #(addrDesc, nextIndx, lineNum) */
  ^tuple(addrDesc, 
     longAt(nextIndxRef, 0),
     wordAt(lineNumRef, 0), 
     byteAt(colNumRef, 0) 
     );
             
}


!!

/* 6/17/1992 11:03 - PUBLIC
  Return Information about a name.
  ^(moduleDesc, addrDesc) or nil if error occurred;
*/
Def getAddrFromName(self, nameStr | moduleDescRef, addrDescRef, lineDescRef, colDescRef)
{ 
  if pcallLock(self) then
    moduleDescRef := new(Struct, 4);
    addrDescRef := new(Struct, 4);
    lineDescRef := new(Struct, 4);
    colDescRef := new(Struct, 4);                                
    lastError := pcall(procs[#SYMGETADDRFROMNAME], asciiz(nameStr) /* NULL terminated */, 
                  asInt(0) /* 0 = SYM_CODE_ADDR */, moduleDescRef,
                  addrDescRef, lineDescRef, colDescRef);  
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  if lastError <> GOOD then
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;
  endif;
  ^tuple(longAt(moduleDescRef, 0), /* Module Descriptor */
         longAt(addrDescRef, 0));   /* Address Descriptor of input string */
}
!!

/* 6/16/1992 16:11 - PUBLIC
  Get the address of the symbol. Return an address descriptor or nil.
  Caller needs to deallocate the address descriptor when done.
*/
Def getSymbolAddress(self, symDesc | addrDesc)
{ 
  if not(addrDesc := createAddress(AddressLibClass$Inst)) then
    ^nil; /* something is awfully wrong - better exit */
  endif;
  if pcallLock(self) then
    lastError := pcall( procs[#SYMGETSYMBOLADDRESS], symDesc, addrDesc);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  
  if lastError <> GOOD then
    destroyAddress( AddressLibClass$Inst, addrDesc ) ;
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;
  endif;      
  ^addrDesc;        
}
!!

/* 6/16/1992 15:43 - PUBLIC
  Get only function symbol descriptors of the input symDesc.
*/
Def getSymbolChildFunc(self, parentSymDesc | symTypeRef, symRef)
{ 
  if pcallLock(self) then
    symRef := new(Struct, 4);
    symTypeRef := new(Struct, 4); 
    lastError := pcall(procs[#SYMGETSYMBOLCHILD], parentSymDesc, symTypeRef, symRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  
  if lastError <> GOOD then
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;
  endif;
  /* Now check for only function type to return */
  /*
  *** Notes: symType = SYM_FUNC : 2 - defined in server header 
   */
  if (longAt(symRef, 0) <> 0) cand (longAt(symTypeRef, 0) = 2) then 
    ^longAt(symRef, 0);
  endif;
  ^nil;  
}
!!

/* 6/16/1992 11:13 - PUBLIC
  Get the name of the symDesc. 
*/
Def getSymbolName(self, symDesc | nameRef, longCAddr, nameStr)
{ 
  /* NOTES: 10/07/93 - Nghia
  ** If symDesc is NULL_SYMBOL return nil - don't bother to call server
  */ 
  if (not(symDesc) cor (symDesc = 0)) then
    ^nil;
  endif;
  nameRef := new(Struct, 4); 
  longCAddr := nil;
  if pcallLock(self) then
    lastError := pcall( procs[#SYMGETSYMBOLNAME], symDesc, nameRef );
    longCAddr := longAt(nameRef, 0);       
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;    
  endif;
    
  /* if error or nameRef is NULL then exit */
  if (lastError <> GOOD) then
    if longCAddr then 
      cFree(MallocLibClass$Inst, longCAddr);
    endif;  
    ^nil; 
  endif; 
  /* Convert string to Actor string and Free alloc string */
  nameStr := copyFromLong(new(Struct, 128), longCAddr);   
  cFree(MallocLibClass$Inst, longCAddr);
  ^removeNulls(physicalString(nameStr));
}
!!

/* 4/23/1992 14:29  - PUBLIC 
  Remove all symbols in the symbol server.
*/
Def removeSymbols(self)
{ 
  if pcallLock(self)
  then
    lastError = pcall( procs[#SYMREMOVESYMBOLS]) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    ^nil;
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,CHECK_MODE);
  ^lastError;  
}
!!

/* PRIVATE
   Define all entry points for library as prototypes.
*/
Def addImportProcs(self)
{
  add( self , #INITCSERVER,               1, #(0, 0)                );
  add( self , #SYMGETADDRFROMNAME,        1, #(1, 0, 1, 1, 1, 1)    );
  add( self , #SYMGETALLMODULES,          1, #(1, 0, 1)             );
  add( self , #SYMGETMODULE,              1, #(1, 1, 1)             );
  add( self , #SYMGETFUNC,                1, #(1, 1, 1, 1, 1)       );
  add( self , #SYMGETFUNCBYNAME,          1, #(1, 1, 1)             );
  add( self , #SYMGETSYMBOLNAME,          1, #(1, 1)                );
  add( self , #SYMGETSYMBOLADDRESS,       1, #(1, 1)                );  
  add( self , #SYMGETLINENUM,             1, #(1, 0, 1, 1, 1, 1)    );
  add( self , #SYMGETLINENUMBYINDEX,      1, #(1, 1, 1, 1, 1, 1)    );
  add( self , #SYMGETLDRSTATS,            1, #(1, 1, 1, 1)          );
  add( self , #SYMGETMODULEDESC,          1, #(1, 1, 1)             );
  add( self , #SYMGETMODULEREF,           1, #(1, 1)                );
  add( self , #SYMGETMODULELISTHEAD,      1, #(1)                   );
  add( self , #SYMGETMODULETIMESTAMP,     1, #(1, 1)                );
  add( self , #SYMGETSYMBOLPARENT,        1, #(1, 1, 1)             );
  add( self , #SYMGETSYMBOLCHILD,         1, #(1, 1, 1)             );
  add( self , #SYMGETSYMBOLSIBLING,       1, #(1, 1, 1)             );
  add( self , #SYMGETSYMBOLFROMCONTEXT,   1, #(1, 1, 1, 1, 1)       );
  add( self , #SYMMAPLINENUM2FUNCORBLOCK, 1, #(1, 0, 1, 1)          );
  add( self , #SYMMAPADDR2LINENUMMODULE,  1, #(1, 1, 1, 1, 1, 1, 1) );
  add( self , #SYMMAPADDR2LINENUM,        1, #(1, 1, 1, 1, 1, 1)    );
  add( self , #SYMMAPADDR2SYMBOL,         1, #(1, 1, 1, 1, 1, 1, 1) );
  add( self , #SYMREMOVESYMBOLS,          1, nil                    );
  add( self , #SYMGETLINENUMINORDER,      1, #(1, 0, 1, 1, 1)       );
  add( self , #SYMGETMODULEALIAS,         1, #(1, 1)                );
}!!

/* PUBLIC
   Returns nil or 
#( moduleDesc, moduleAddrDesc, lineNum, colNum, lineAddrDesc, lineNumDesc ).

   NOTA BENE: The caller must deallocate address descriptors via
              destroyAddress(AddressLibClass$Inst, addrDesc).
*/
Def addr2lineAndModule(self, intialAddrDesc 
   | moduleDRef, moduleAddrD, lineRef, colRef, lineAddrD, lineNumDRef)
{
  moduleAddrD := createAddress( AddressLibClass$Inst ) ;
  if not(moduleAddrD)
  then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError( AddressLibClass$Inst ) ;
    ^nil;
  endif ;

  lineAddrD   := createAddress( AddressLibClass$Inst ) ;
  if not(lineAddrD)
  then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError( AddressLibClass$Inst ) ;
    destroyAddress( AddressLibClass$Inst, moduleAddrD ) ;
    ^nil;
  endif ;

  moduleDRef  := new( Struct, 4 ) ;
  lineRef     := new( Struct, 4 ) ;
  colRef      := new( Struct, 4 ) ;
  lineNumDRef := new( Struct, 4 ) ;
  
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMMAPADDR2LINENUMMODULE],
                      intialAddrDesc,
                      moduleDRef, 
                      moduleAddrD, 
                      lineRef,   /* *U16 */
                      colRef,    /* *U8  */
                      lineAddrD, 
                      lineNumDRef
                    ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;


  if (lastError <> GOOD) then
    /* Since the address might not have any symbolic information */
    if ((lastError <> ER_LINENUM_NOT_FOUND) cand
      (lastError <> ER_ADDRESS_NOT_FOUND) cand
      (lastError <> ER_NO_LINENUMS_ADDED)) then
      displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP); 
    endif;  
    /* Get rid of the created address descriptors */
    destroyAddress( AddressLibClass$Inst, moduleAddrD ) ;
    destroyAddress( AddressLibClass$Inst, lineAddrD ) ;
    ^nil;
  endif;
    
  /*
  ** Return #(moduleDesc, moduleAddrDesc, lineNum, colNum, lineAddrDesc, lineNumDesc)
  */
  ^tuple( longAt( moduleDRef, 0 ),
          moduleAddrD,
          wordAt( lineRef, 0 ),
          byteAt( colRef,  0 ) ,
          lineAddrD,
          longAt( lineNumDRef, 0 ));
 
}
!!

/* PUBLIC
   Returns nil or 
   #(lineNum, colNum, addrDesc, lineNumDesc ).

   NOTE: The caller must deallocate address descriptors via
              destroyAddress(AddressLibClass$Inst, addrDesc).
*/
Def addr2Linenum(self, moduleDesc, addrDesc 
   | lineRef, colRef, addrRangeDesc, lineNumDRef)
{
  if not(addrRangeDesc := createAddress(AddressLibClass$Inst))
  then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError( AddressLibClass$Inst ) ;
    ^nil;
  endif ;

  lineRef     := new( Struct, 4 ) ;
  colRef      := new( Struct, 4 ) ;
  lineNumDRef := new( Struct, 4 ) ;
  
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMMAPADDR2LINENUM],
                      addrDesc,
                      moduleDesc, 
                      lineRef,   /* *U16 */
                      colRef,    /* *U8  */
                      addrRangeDesc, 
                      lineNumDRef
                    );
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    destroyAddress( AddressLibClass$Inst, addrRangeDesc );
    displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    ^nil;  
  endif;  
  /* RETURN: #(lineNum, colNum, addrDesc, lineNumDesc)*/
    ^tuple( wordAt( lineRef, 0 ),
            byteAt( colRef,  0 ) ,
            addrRangeDesc,
            longAt(lineNumDRef, 0));
}
!!

/* PUBLIC
   funcDescriptor -> name X class X stackSize X address-range
     NOTES: Caller must deallocate addressDescriptor! [requires AddressLib]
*/
Def getFunctionInfo(self, functionDescriptor
                   | stringRef classRef stackSizeRef addrDesc)
{
  if not(addrDesc  := createAddress(AddressLibClass$Inst)) then
    ^nil;
  endif;

  stringRef    := new( Struct, 256 );
  classRef     := new( Struct, 4 );
  stackSizeRef := new( Struct, 4 );

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETFUNC],
                      functionDescriptor, 
                      stringRef,    /* LPSTR */
                      classRef,     /* *U16  */
                      stackSizeRef, /* *U32  */
                      addrDesc ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
  /* RETURN #(name, class, stackSize, addressDesc-range) */
    ^tuple( removeNulls(physicalString(stringRef)), 
            wordAt(classRef, 0 ),
            longAt(stackSizeRef, 0),
            addrDesc);
  endif;
  destroyAddress( AddressLibClass$Inst, addrDesc ) ;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* 4/2/1992 0:42 - PUBLIC
  Given a moduleDesc and requested line number, get its actual line number
  information.
  return #(addrDesc, lineNum, colNum, lineNumDesc) or nil
  Note: Caller must destroy addrDesc when done.
*/
Def getLineNumInfo(self, moduleDesc, lineNum 
| addrDesc, lineNumRef, colNumRef, nextIdxRef )
{ 
  /* Create addrDesc */
  if not(addrDesc := createAddress(AddressLibClass$Inst)) then
    lastError := lastError( AddressLibClass$Inst ) ;
    clearError( AddressLibClass$Inst ) ;
    ^nil;
  endif ;
  lineNumRef := new(Struct, 4);
  colNumRef  := new(Struct, 4);
  nextIdxRef := new(Struct, 4);
  
  if pcallLock(self) then
    lastError := pcall(procs[#SYMGETLINENUM], 
    asLong(moduleDesc),     /* U32 */
    lineNum,                /* U16 */
    addrDesc,
    lineNumRef,
    colNumRef, 
    nextIdxRef);
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError <> GOOD) then
    if ((lastError <> ER_LINENUM_INDEX_TOO_LARGE) cand
      (lastError <> ER_LINENUM_NOT_FOUND) cand
      (lastError <> ER_NO_LINENUMS_ADDED)) then
      displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
    endif;
    destroyAddress( AddressLibClass$Inst, addrDesc);
    ^nil;               
  endif;
  /* RETURN #(addrDesc, lineNum, colNum, lineNumDesc) */  
  ^tuple(addrDesc, 
       wordAt(lineNumRef, 0), 
       byteAt(colNumRef, 0), 
       longAt(nextIdxRef, 0));     
}
!!

/* PUBLIC
   ModuleName -> moduleDescriptor
   NOTES: pathName must be as compiler stuffs in loadmodule (less module name).
*/
Def getModuleDescriptor(self, moduleName, pathName | descRef)
{
  if not( pathName ) then
    pathName := "" ;
  endif ;
  descRef := new( Struct, 4 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETMODULEDESC],
                      asciiz(moduleName), asciiz(pathName), descRef ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD)  then
    ^longAt( descRef, 0 )
  endif;
  ^nil;
}
!!

/* PUBLIC
   moduleDescriptor -> name X address-range
   NOTES: Caller must deallocate addressDescriptor! 
   09/08/93 - Nghia
   Filter out ER_ADR_END_ADDR_TOO_SMALL for data module.
*/
Def getModuleInfo(self, moduleDescriptor 
                 | stringRef addrDesc strPos aString)
{
  addrDesc  := createAddress(AddressLibClass$Inst);
  if not(addrDesc) then
    lastError := lastError(AddressLibClass$Inst);
    clearError(AddressLibClass$Inst);
    ^nil;
  endif ;
  stringRef := new(Struct, 256);
  putByte(stringRef, 0, 0);

  if pcallLock(self) then
    lastError := pcall( procs[#SYMGETMODULE],
                      moduleDescriptor, stringRef, addrDesc ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if ((lastError <> GOOD) cand 
     (lastError <> ER_ADR_END_ADDR_TOO_SMALL)) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    destroyAddress( AddressLibClass$Inst, addrDesc);
    ^nil;
  else
    /* Get module name */
    aString := physicalString( stringRef ) ;
    if (strPos := indexOf(aString, asChar(0), 0)) then
       aString := subString(aString, 0, strPos);
    endif;
    ^tuple(aString, addrDesc);
  endif;
}
!!

/* PUBLIC
   -> LoadModuleDescriptor
*/
Def getModuleListHead(self | descRef)
{
  descRef := new( Struct, 4 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETMODULELISTHEAD], descRef ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
     ^longAt( descRef, 0 );
  endif;
  /* Filter out the error */
  if (lastError <> ER_SYM_NO_MODULES_LOADED) then
    displayError(ErrorTextLibClass$Inst,lastError,CHECK_MODE);
  endif;  
  ^nil;
}
!!

/* PUBLIC
   moduleDescriptor -> source/loadmod pathname string
*/
Def getModuleSrcPath(self, moduleDescriptor | stringRef)
{
  stringRef := new( Struct, 256 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETMODULEREF], moduleDescriptor, stringRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^removeNulls(physicalString(stringRef));
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC
  moduleDescriptor -> timestamp
*/
Def getModuleTimestamp(self, modSym | descRef)
{
  descRef := new( Struct, 8 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETMODULETIMESTAMP], modSym, descRef ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
     ^descRef  
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC
   symbolDescriptor -> parentDesc x type
*/
Def getSymbolParent(self, symbolDescriptor | descRef typeRef)
{
  descRef := new( Struct, 4 ) ;
  typeRef := new( Struct, 4 ) ;
  putWord( typeRef, SYM_UNDEFINED, 0 ) ;

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETSYMBOLPARENT],
                      symbolDescriptor, typeRef, descRef ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^tuple( longAt(descRef, 0), wordAt(typeRef, 0));
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC
   symbolDescriptor -> symbolDescriptor x type
*/
Def getSymbolSibling(self, symbolDescriptor | descRef typeRef)
{
  descRef := new( Struct, 4 ) ;
  typeRef := new( Struct, 4 ) ;
  putWord( typeRef, SYM_UNDEFINED, 0 ) ;

  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETSYMBOLSIBLING],
                      symbolDescriptor, typeRef, descRef ) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^tuple( longAt(descRef, 0), wordAt(typeRef, 0));
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC -- returns blockDesciptor on success
*/
Def line2blockDescriptor(self, moduleDescriptor, lineNum
                        | typeRef, symDescRef)
{
  typeRef    := new( Struct, 4 ) ;
  symDescRef := new( Struct, 4 ) ;
  if pcallLock(self) then
    lastError := pcall( procs[#SYMMAPLINENUM2FUNCORBLOCK],
                      moduleDescriptor, asInt(lineNum), typeRef, symDescRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    ^longAt(symDescRef, 0);
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC -- returns #( symbolDesciptor, type, isGlobal? ) on success
   Where `type' is one of #functionName, #variableName, or #other.
*/
Def symbolFromContext(self, blockDescriptor, name
                    | typeRef, symDescRef, symTypeType, boolRef)
{
  typeRef    := new( Struct, 4 ) ;
  putWord( typeRef, SYM_UNDEFINED, 0 ) ;
  symDescRef := new( Struct, 4 ) ;
  boolRef    := new( Struct, 4 ) ;
  if pcallLock(self)
  then
    lastError := pcall( procs[#SYMGETSYMBOLFROMCONTEXT],
                      blockDescriptor,
                      asciiz(name), typeRef, symDescRef, boolRef) ;
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  if (lastError = GOOD) then
    symTypeType := wordAt(typeRef, 0) ;
    select
      case (symTypeType = 2) /* SYM_FUNCTION */
      is symTypeType := #functionName ;
      endCase
      case (symTypeType = 4) or (symTypeType = 5) /* SYM_GLOBAL_VAR or SYM_LOCAL_VAR */
      is symTypeType := #variableName ;
      endCase
      case (symTypeType = 12)
      is symTypeType := #publicLabel ;
      endCase
      default 
        symTypeType := #other ;
    endSelect;
    ^tuple( longAt(symDescRef, 0), symTypeType, (byteAt(boolRef, 0) <> 0) )
  endif;
  displayError(ErrorTextLibClass$Inst,lastError,FORCE_POPUP);
  ^nil;
}
!!

/* PUBLIC                  [Ron, 5/6/93, For Variable Window Enhancements] 
 *
 * Input: varStr is the name of a variable -- either a simple alphanumeric
 *        string or a symbol name as described in the manual, of form
 *        #module#function#variable.  The variable name is the root variable
 *        (the caller must strip off any "." or "->" dereference chain before
 *        calling this method.
 *
 * Output: a symbol descriptor or nil (if not found)
 *
 * Processing:
 *    There are three possibilities, each of which is handled differently
 *    by different modules.  The variable may be:
 *       a) a static variable with specified context: #mod#fun#var
 *       b) a static or stack-based variable in the current stack context, 
 *          in which case either "varname" or "#varname" is used
 *       c) a stack-based variable outside the current stack context
 *    The most likely situation, and the primary incentive for this 
 *    function, is (a), which is handled entirely by SymGetAddrFromName 
 *    from the symbol library.  The other situations must be dealt with 
 *    similar to the way "Inspect Variable" from sourcecb.cls handles it, 
 *    or AddVar from the variable server handles it.
 */!!

Def varNameToSymDesc(self, varStr | tmptup tmpInd curModule curFunc varName curLine symbolDesc blockDesc)
{
   if (not(varStr)) then
      ^nil;
   endif;
   if (varStr[0] <> '#') then
      /*
       * If it doesn't start with a '#', look it up the way the source
       * presenter does.
       */
      curModule := getCurrentModuleFromContext(StackLibClass$Inst);
      if (curModule) then
         curLine := getCurrentLineNumFromContext(StackLibClass$Inst);
         if (curLine) then
            blockDesc := primeLine2blockDescriptor(self, curModule, curLine);
            if (blockDesc) then
               symbolDesc := primeSymbolFromContext(self, blockDesc, varStr);
            endif;
         endif;
      endif;
      if (symbolDesc) then
         ^symbolDesc[0];
      endif;
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_VAR_CANT_FIND_VAR, FORCE_POPUP, varStr, nil, nil);
      ^nil;
   endif;
   /* try the best, preferred approach: SymGetAddrFromName */
   symbolDesc := varDescFromName(self, varStr);
   if (symbolDesc) then
      ^symbolDesc ;
   endif;
   /*
    * That didn't work.  Next, try doing things like AddVar does
    * in the varsrv library's varcli.c.  We have to split names
    * of form [#module[#func]]#varname or #func#varname up 
    * ourselves in that case.
    */
   tmptup := splitSymbol(self, varStr); /* returns #(token1 token2 varName) */
   if (not(tmptup)) then
      ^nil;
   endif;
   varName := tmptup[2];
   if (not(varName) cor (size(varName) < 1)) then
      ^nil;
   endif;
   if (tmptup[0]) cand (tmptup[1]) then /* #mod#fun#var */
      curModule := getModuleDescriptor(self, tmptup[0], "");
      if (curModule) then
         curFunc := getFuncByName(self, curModule, tmptup[1]);
         if (not(curFunc)) then
            displayFormattedError(ErrorTextLibClass$Inst, 
               ER_VAR_CANT_FIND_FUN, FORCE_POPUP, asciiz(tmptup[1]), nil, nil);
            ^nil;
         endif;
         symbolDesc := primeSymbolFromContext(self, curFunc, varName);
      else
         displayFormattedError(ErrorTextLibClass$Inst, 
            ER_VAR_CANT_FIND_MOD, FORCE_POPUP, asciiz(tmptup[0]), nil, nil);
         ^nil;
      endif;
   else /* #mod#var or #fun#var or #var */
      if (tmptup[0]) then
         curModule := getModuleDescriptor(self, tmptup[0], "");
         if (curModule) then
            /* #mod#var */
            symbolDesc := primeSymbolFromContext(self, curModule, varName);
         else /* #fun#var */
            curModule := getCurrentModuleFromContext(StackLibClass$Inst);
            if (curModule) then
               symbolDesc := getFuncByName(self, curModule, tmptup[0]);
               if (symbolDesc) then
                  curFunc := symbolDesc;
                  symbolDesc := primeSymbolFromContext(self, curFunc, varName);
               endif;
            endif;
         endif;
      else
         /* retry with just the name (without the #) */
         ^varNameToSymDesc(self, varName);
      endif;
   endif;
   if (symbolDesc) then
      ^symbolDesc[0];
   endif;
   displayFormattedError(ErrorTextLibClass$Inst, 
      ER_VAR_CANT_FIND_VAR, FORCE_POPUP, varStr, nil, nil);
   ^nil;
}
!!

/* PRIVATE                [Ron, 5/6/93, For Variable Window Enhancements] 
 *
 * Return a symbol descriptor from a name, or nil if not found
 */
Def varDescFromName(self, nameStr | varSymbolDesc, addrDescRef, lineDescRef, colDescRef)
{ 
   if (not(nameStr)) then
      ^nil;
   endif;
   if pcallLock(self) then
      varSymbolDesc := new(Struct, 4);
      addrDescRef := new(Struct, 4);
      lineDescRef := new(Struct, 4);
      colDescRef := new(Struct, 4);                                
      lastError := pcall(procs[#SYMGETADDRFROMNAME], asciiz(nameStr),
                  asInt(1) /* 1 = SYM_DATA_ADDR */, varSymbolDesc,
                  addrDescRef, lineDescRef, colDescRef);  
      pcallUNLock(self);
   else
      lastError := ERR_PCALL_RECURSION;
   endif;
   if lastError <> GOOD then
      ^nil;
   endif;
   ^longAt(varSymbolDesc, 0);
}
!!

/* PRIVATE                [Ron, 5/6/93, For Variable Window Enhancements] 
 *
 * Given a '#'-based symbol name, split it up into module, function, varname
 *
 * Note: findStrings not used here since I'm not sure how it deals
 *       with error cases like "#mod#", and since we have to be sure
 *       that varName goes in the third position when it can be in
 *       the first, second, or third position of the input.
 *
 * Return #(module function varname 0)
 */
Def splitSymbol(self, varStr | tmpStr tmpInd token1 token2 varName)
{
   /*
    * varStr is of form [#module[#func]]#varname OR
    * of form #func#varname -- split up into pieces
    */
   if (not(varStr)) then
      ^nil;
   endif;
   tmpStr := subString(varStr,1,size(varStr)); /* pass up first '#' */
   tmpInd := indexOf(tmpStr,'#',0);
   if (tmpInd) then
      /* At least one token before variable name */
      token1 := subString(tmpStr,0,tmpInd); /* module or function name */
      tmpStr := subString(tmpStr,tmpInd + 1,size(tmpStr));
      if (not(tmpStr)) then
         displayFormattedError(ErrorTextLibClass$Inst, 
            ER_VAR_END_PROB, FORCE_POPUP, nil, nil, nil);
         ^nil;
      endif;
      tmpInd := indexOf(tmpStr,'#',0);
      if (tmpInd) then
         /* There is a second token before varName too */
         token2 := subString(tmpStr,0,tmpInd); /* function name */
         tmpStr := subString(tmpStr,tmpInd + 1,size(tmpStr));
         if (tmpStr) then
            /* We got #module#func#varname */
            varName := copy(tmpStr);
         else
            displayFormattedError(ErrorTextLibClass$Inst, 
               ER_VAR_END_PROB, FORCE_POPUP, nil, nil, nil);
            ^nil;
         endif;
      else
         /* We got #module#varname */
         varName := copy(tmpStr);
      endif;
   else
      varName := copy(tmpStr);
   endif;

   ^tuple( token1, token2, varName, 0 )
}!!

/* PUBLIC          [Ron, 5/6/93, added for Variable Window Enhancements]
 *
 * Get function descriptor from a function name and a module descriptor
 * 
 */
Def getFuncByName(self, modDesc, funcName | symRef)
{ 
   if pcallLock(self) then
      symRef := new(Struct, 4);
      lastError := pcall(procs[#SYMGETFUNCBYNAME], modDesc, 
         asciiz(funcName), symRef);
      pcallUNLock(self);
   else
      lastError := ERR_PCALL_RECURSION;
   endif;

   if lastError <> GOOD then
      ^nil;
   endif;
   if (longAt(symRef, 0) <> 0) then 
      ^longAt(symRef, 0);
   endif;
   ^nil;  
}
!!
