/** tables.c - build and maintain internal tables
*
*       string hash routines, type hash routines, and
*       compacted segment information
*
* History:
*  01-Feb-1994 HV Move messages to external file.
*
*/


#include "compact.h"
#include <getmsg.h>     // external error message file
#include "writebuf.h"

#include "pdb.h"

#define CB_ATOMIC 8100  // 8100 gives room for 8 allocs + header in 1 segment

#define INITMODTABSIZE      12000   // initial entries
#define REALLOCMODTABSIZE   6000    // additional entries
#define TYPEBLOCKSIZE       0x10000 // size of compacted types sub table
#define SYMBLOCKSIZE        4096    //
#define NameHashBlockSize   4096
#define OffHashBlockSize    4096
#define MINHASH             6       // minimum size of hash table
#define HASH_STRING         4096    // String hash table size
#define HASH_TYPE           4096    // Type hash table size
#define HASH_SYM            4096    // Symbol hash table size
#define HASH_COMDAT         4096    // ComDat hash table size
#define HASH_NAME           4096    // Globals subsection hash size

#define DLIST_INC     20           // number of derived classes
#define DCLASS_INC    128 // number of structures


//  This hash table is used to hash local structures, class, unions
//  and enums so that they can be quickly found for satisfying forward
//  references.  The hash is the sum of characters in the name.


HSFWD **HTLocalFwd = NULL;
HSFWD **HTGlobalFwd = NULL;


//  This hash table is used to hash recursive type records that have
//  been added to the global symbol table.  The hash is the sum of bytes
//  of all bytes that do not contain a recursive type index.


typedef struct HSTYPE {
    uchar      *Type;
    CV_typ_t    GlobalIndex;
    struct      HSTYPE *Next;
} HSTYPE;
HSTYPE **HTType = NULL;
ushort *HTTypeCnt = NULL;



//  This hash table is used to hash non-recursive type records that have
//  been added to the global symbol table.  The hasf is the sum of bytes
//  of all bytes in the type record.


typedef struct  HSSTRING {
    CV_typ_t    CompactedIndex;
    TYPPTR      TypeString;
    struct      HSSTRING *Next;
} HSSTRING;
HSSTRING **HPDBtring = NULL;
ushort *HPDBtringCnt = NULL;


//  These hash tables are used to hash the public symbols and the module
//  level symbols that have been moved from the module to the global
//  symbol table.


typedef struct  GLOBALSYM {
    struct GLOBALSYM *Next;
    ulong   sumName;
    ulong   uoff;
    ushort  indName;
    ushort  seg;
    uchar   Sym[];
} GLOBALSYM;
GLOBALSYM **HPDBym = NULL;
GLOBALSYM **HTPub = NULL;
GLOBALSYM **HTLoc = NULL;


typedef struct HSCOMDAT {
    struct  HSCOMDAT *Next;
    ulong   Offset;
    ushort  Seg;
    ushort  Type;
    ushort  Sum;
    uchar   Name[1];
} HSCOMDAT;
HSCOMDAT **HTComDat = NULL;



typedef struct DCLASS {
    ushort BClass;      // base class type index
    ushort Count;       // number of classes derived from this class
    ushort Max;         // maximum list size
    ushort *List;       // pointer to list of derived classes
} DCLASS;
typedef DCLASS *PDCLASS;



//  This structure is the primary module level type index table.  There is
//  a list of these that points to the secondary tables.  Each entry in the
//  secondary table points to the type record for the corresponding type
//  index.  Initially the type record as read in from the exe file is
//  is pointed to.  As type records are entered into or found in the global
//  types table, the type record in the global table is pointed to by the
//  secondary table entry.  This double table format is used because some
//  compilers emit a skip record for reservind space in the types table.


struct BlockListEntry {
    ushort Low;                     // low type index in the seconday table
    ushort High;                    // high type index in the seconday table
    TENTRY *ModuleIndexTable;       // pointer to the seconday table
    struct BlockListEntry *Next;    // pointer to the next primary table
};




#define cBucketSize 10
#define cbMaxAlloc 0xFFE0
#define culEct (cBucketSize + cBucketSize / 4)
#define OVFL_C (cBucketSize / 2)


typedef struct _SOH {
    ulong uoff;
    ulong ulHash;
} SOH;  // Symbol Offset/Hash structure

//  Entry in the Chain Overflow Table
//  This structure is used when the number of entries hashed to a
//  single bucket exceeds the estimate of cBucketSize


typedef struct _OVFL {
    struct _OVFL *pOvflNext;               // pointer to next chain
    SOH           rgsoh [OVFL_C];          // array of symbol offsets
} OVFL;
typedef OVFL *POVFL;


// Entry int the Chain Table

typedef struct _ECT {
    ushort  culSymbol;                      // count of symbols in chain
    POVFL   pOvflNext;                      // pointer to overflow chain
    SOH     rgsoh[culEct];                  // array of symbol offsets
} ECT;
typedef ECT *PECT;

#define cectMax  (cbMaxAlloc / sizeof (ECT))    // maximum chains per segment
#define cecetMax (cbMaxAlloc / sizeof (OVFL))   // maximum extended chains per seg
#define cpecetMax 10

bool_t NoMoTypes = FALSE;

LOCAL   void    AddDerivationListsToTypeSegment (void);
LOCAL   ushort  _fastcall StringHash (uchar *);
LOCAL   void    AddToDerivationList (CV_typ_t, CV_typ_t);
LOCAL   int     _fastcall TypeHash (TENTRY *);
LOCAL   void    CleanUpModuleIndexTable (void);
LOCAL   GPS_t   InGlobalSym (SYMPTR);
LOCAL   void    BuildHash (LPFNHASH, ushort *, ulong *, VBuf *, ulong, GLOBALSYM **);
LOCAL   void    BuildSort (ushort *, ulong *, VBuf *, ulong, GLOBALSYM **);
INLINE  void    _fastcall AddTypeEntry (uchar *, bool_t, bool_t, CV_typ_t, ushort);
LOCAL   void    MapPreComp (plfPreComp);
LOCAL   PMOD    FindModule (char *);
LOCAL   FWD_t   FindLocalFwd (ushort, char *, HSFWD **, bool_t isnested);
LOCAL   FWD_t   FindGlobalFwd (ushort, char *, HSFWD **);
INLINE  void    HashFwdGlobal (TYPPTR, CV_typ_t);
LOCAL   ushort  _fastcall SubHash (TENTRY *OldEntry, ushort iitem);
LOCAL BOOL fAssert;
LOCAL PDB *ppdb;                       // current database
LOCAL TPI *ptpi;

PDCLASS DLists;

ushort  NextInDerivation;
ushort  MaxDerivation;

uchar **pGType[65536 / GTYPE_INC];

CV_typ_t NewIndex = CV_FIRST_NONPRIM;  // start for new types
ushort LastGlobalTableIndex;           // last possible index
ushort AddNewSymbols;                  // need to add typedefs?
ulong  cGlobalSym;                     // total number of global symbols
ulong  cbGlobalSym;                    // total number of bytes in global symbols
ulong  cPublics;                       // total number of publics
ulong  cbPublics;                      // total number of bytes in publics
ulong  cGlobalDel;                     // total number of global symbols
ushort cSeg;
ushort segnum[MAXCDF];
ulong  cStaticSym;                     // total number of static symbols refs
ulong  cbStaticSym;                    // total number of bytes in static ref table

ulong AlignTable ( GLOBALSYM **, int );

ushort usInitCount;

extern uchar **ExtraSymbolLink;
extern uchar *ExtraSymbols;
extern ushort UDTAdd;
extern CV_typ_t usCurFirstNonPrim;     // The current first non primitive type index
extern ulong ulCVTypeSignature;        // The signature from the modules type segment

ulong InitialTypeInfoSize;
CV_typ_t MaxIndex;                     // Maximum index for module

TENTRY *ModNdxTab;
CV_typ_t SizeModNdxTab;

VBuf SymBuf;
VBuf TypeBuf;


// A null LF_ARGLIST entry created at init time.

TENTRY *ZeroArg;

CV_typ_t PreviousMaxIndex;


INLINE void PostClearModNdx(void) {

    if (PreviousMaxIndex) {
        memset(ModNdxTab, 0, PreviousMaxIndex * sizeof(TENTRY));
        PreviousMaxIndex = 0;
    }
}

/**     InitializeTables
 *
 *      Initialize the various tables and allocate space on the heap
 *      for them
 *
 *      Entry   none
 *
 *      Exit    tables initialized
 *
 *      Returns none
 *
 */

void InitializeTables (void)
{
    plfArgList plf;

    HTType = (HSTYPE **) CAlloc (HASH_TYPE * sizeof (HSTYPE *));
    HTTypeCnt = (ushort *) CAlloc (HASH_TYPE * sizeof (ushort));
    HPDBtring = (HSSTRING **) CAlloc (HASH_STRING * sizeof (HSSTRING *));
    HPDBtringCnt = (ushort *) CAlloc (HASH_STRING * sizeof (ushort));
    HPDBym = (GLOBALSYM **) CAlloc (HASH_SYM * sizeof (GLOBALSYM *));
    HTPub = (GLOBALSYM **) CAlloc (HASH_SYM * sizeof (GLOBALSYM *));
    HTLoc = (GLOBALSYM **) CAlloc (HASH_SYM * sizeof (GLOBALSYM *));
    HTComDat = (HSCOMDAT **) CAlloc (HASH_COMDAT * sizeof (HSCOMDAT *));
    DLists = (PDCLASS) CAlloc (DCLASS_INC * sizeof (DCLASS));
    HTLocalFwd = (HSFWD **)CAlloc (HASH_FWD * sizeof (HSFWD *));
    HTGlobalFwd = (HSFWD **)CAlloc (HASH_FWD * sizeof (HSFWD *));
    MaxDerivation = DCLASS_INC;
    LastGlobalTableIndex = 0;
    VBufInit (&SymBuf, SYMBLOCKSIZE);
    VBufInit (&TypeBuf, TYPEBLOCKSIZE);

    // create and initialize the null argument list

    ZeroArg = CAlloc (sizeof (TENTRY));
    ZeroArg->TypeString = Alloc (offsetof (lfArgList, arg) + LNGTHSZ);
    ZeroArg->flags.IsNewFormat = TRUE;

    // Create the type string

    ((TYPPTR)(ZeroArg->TypeString))->len = offsetof (lfArgList, arg) + LNGTHSZ;
    plf = (plfArgList) (ZeroArg->TypeString + LNGTHSZ);
    plf->leaf = LF_ARGLIST;
    plf->count = 0;
}




/**
 *
 *  CleanUpTables
 *
 *  Free the various tables that have been allocated
 *
 */

void CleanUpTables (void)
{

    AddDerivationListsToTypeSegment ();

    free (DLists);
    free (HTType);
    free (HPDBtring);
//  free (HPDBym);
    free (HTComDat);
    CleanUpModuleIndexTable();
    if (ppdb) {
        if (ptpi) {
            fAssert = TypesClose(ptpi);
        }

        fAssert |= PDBClose(ppdb);

        DASSERT(fAssert);
    }
}


void *AllocForHT ( uint cb ) {
    void *pvRet = NULL;

    static uint   cbLocal = 0;
    static uchar *pbLocal = NULL;

    if ( cbLocal < cb ) {
        DASSERT ( cb < CB_ATOMIC );
        pbLocal = Alloc ( CB_ATOMIC );
        cbLocal = CB_ATOMIC;
    }

    pvRet = pbLocal;
    cbLocal -= cb;
    pbLocal += cb;

    return pvRet;
}

/**     MatchIndex - find the compacted index of a non-recursive type
 *
 *      MatchIndex (OldEntry)
 *
 *      Entry   OldEntry = pointer to type entry
 *
 *      Exit    OldEntry->TypeString added to global type table if not present
 *              OldEntry->CompactedIndex = global type index
 */

COUNTER (cnt_MatchIndex1);
COUNTER (cnt_MatchIndex2);

void _fastcall MatchIndex (TENTRY *OldEntry)
{
    ushort      i;
    HSSTRING   *j;
    plfClass    plf;
    TYPPTR      pType;
    uchar      *pName;
    HSFWD      *pHash;

    DASSERT (OldEntry->flags.IsNewFormat);

    // search through nonrecursive hash list looking for matching entry

    i = StringHash (OldEntry->TypeString);

    for (j = HPDBtring[i]; j != NULL; j = j->Next) {
        if (j->TypeString != NULL) {
            // a intermodule forward reference will end up having a
            // null pointer to the type string when it is replaced

            if (memcmp ((uchar *)j->TypeString, OldEntry->TypeString,
              j->TypeString->len + LNGTHSZ) == 0) {
                OldEntry->CompactedIndex = j->CompactedIndex;
                OldEntry->flags.IsMatched = TRUE;
                FreeAllocStrings (OldEntry);
                if (OldEntry->flags.LargeList) {
                    free (OldEntry->IndexUnion.IndexString);
                    free (OldEntry->GlobalIndexString);
                    OldEntry->flags.LargeList = FALSE;
                }

                return;
            }
        }
    }

    // we did not find the type string in the non-recursive hash table.  We
    // now look to see if there is a forward reference that was added by some
    // other module.  If we find one, we will overwrite the forward reference
    // type string with this one and then rehash the string.

    COUNT (cnt_MatchIndex2);
    pType = (TYPPTR)OldEntry->TypeString;
    switch (pType->leaf) {
        case LF_CLASS:
        case LF_STRUCTURE:
            pName = (uchar *)&((plfClass)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_UNION:
            pName = (uchar *)&((plfUnion)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_ENUM:
            pName = (uchar *)&((plfEnum)&(pType->leaf))->Name;
            break;

        default:
            pName = NULL;
    }
    if (pName != NULL) {
        switch (FindGlobalFwd (pType->leaf, pName, &pHash)) {
            case FWD_none:
                // this is OK since we may not have added this list yet
                break;

            case FWD_local:
                // we should never have looked in the module list
                DASSERT (FALSE);
                break;

            case FWD_global:
                // we do not need to do any thing here but add the new type
                break;

            case FWD_globalfwd:
                OldEntry->CompactedIndex = pHash->index;
                memmove (pHash->pType, pType, pType->len + LNGTHSZ);
                OldEntry->TypeString = (uchar *)pHash->pType;
                goto fwdreplaced;
        }
    }

    // No matching type string found so we add the type to the global types
    // table and add it to the string (non-recursive) hash table,  Note that
    // InsertIntoTypeSegment frees the local type string if it is in
    // allocated memory.

    InsertIntoTypeSegment (OldEntry);

fwdreplaced:
    j = (HSSTRING *)Alloc (sizeof (HSSTRING));
    j->TypeString = (TYPPTR)OldEntry->TypeString;
    j->CompactedIndex = OldEntry->CompactedIndex;
    j->Next = HPDBtring[i];
    HPDBtring[i] = j;
    HPDBtringCnt[i]++;
    if ((j->TypeString->leaf == LF_CLASS) ||
      (j->TypeString->leaf == LF_STRUCTURE)) {
        plf = (plfClass)&(j ->TypeString->leaf);
        if (plf->property.fwdref == FALSE) {
            // if we have a forward reference, then the field list
            // was null.

            DoDerivationList (j->CompactedIndex, plf->field);
        }
    }
}




/**
 *
 *  DoDerivationList
 *
 *  Takes a field specification list and the derived class and adds
 *  it to all the inherited classes given by the list.
 *
 */


void DoDerivationList (CV_typ_t DerivedClass, CV_typ_t FieldSpecList)
{
    TYPPTR      typptr;
    uchar      *plf;
    uchar     **pBuf;
    bool_t      Loop = TRUE;


    if (FieldSpecList == T_NOTYPE)
        return;

    // Get the field list type string

    pBuf = pGType[(FieldSpecList - CV_FIRST_NONPRIM) / GTYPE_INC];
    typptr = (TYPPTR)(pBuf[(FieldSpecList - CV_FIRST_NONPRIM) % GTYPE_INC]);
    DASSERT (typptr->leaf == LF_FIELDLIST);

    // Loop through the real and direct virtual base classes

    plf = (uchar *)&(typptr->data);
    while (Loop) {
        if (*plf >= LF_PAD0) {
            plf += *plf & 0x0f;
        }
        switch (((plfEasy)plf)->leaf) {
            case LF_BCLASS:
                AddToDerivationList (DerivedClass, ((plfBClass)plf)->index);
                plf = (uchar *)&((plfBClass)plf)->offset;
                plf += C7SizeOfNumeric (plf);
                break;

            case LF_VBCLASS:
                AddToDerivationList (DerivedClass, ((plfVBClass)plf)->index);
                plf = (uchar *)&((plfVBClass)plf)->vbpoff;
                plf += C7SizeOfNumeric (plf);
                plf += C7SizeOfNumeric (plf);
                break;

            case LF_INDEX:
                DASSERT (FALSE);

            default:
                Loop = FALSE;
                break;
        }
    }
}



/**
 *
 *  InsertIntoTypeSegment
 *
 *  Inserts a type string into the compacted segment and assigns
 *  it a new index
 *  Uses C7 format type strings
 *
 */


void _fastcall InsertIntoTypeSegment (TENTRY *OldEntry)
{
    ushort              length;
    TYPPTR              pType;
    plfStructure        plf;
    UDTPTR              pSym;
    uint                usSymLen;
    uchar              *Name;
    uchar              *NewSymbol;
    uchar              *pchDest;
    unsigned int        iPad;        // Number of pad bytes needed.
    uint                usNewLength; // Symbol length - LNGTHSZ including pad bytes.
    uchar             **pBuf;

    DASSERT (OldEntry->flags.IsNewFormat);

    BreakOnIndex (NewIndex);

    if (NoMoTypes) {
        OldEntry->CompactedIndex = T_NOTYPE;
#if DBG
        if (DbArray[3]) {
            DumpPartialType (NewIndex, (TYPPTR)(OldEntry->TypeString), FALSE);
        }
#endif
        fflush(stdout);
        return;
    }

    pType = (TYPPTR)(OldEntry->TypeString);
    plf =  (plfStructure)&pType->leaf;
#if DBG
    if (DbArray[2])
        DumpPartialType (NewIndex, pType, FALSE);
#endif

    // add in symbol typedefs for structures; maintaining it as
    // a linked list with ExtraSymbols as the head and ExtraSymbolLink
    // to point to the next symbol. The link field preceeds the actual
    // symbol in memory.

    if ((plf->leaf == LF_STRUCTURE) && AddNewSymbols) {
        Name = ((uchar *)plf) + offsetof (lfStructure, data);
        Name += C7SizeOfNumeric (Name);  // go to the name
        if (*Name != 0 &&
            ((*Name != sizeof ("(untagged)") - 1) ||
            (memcmp ((_TCHAR *)(Name + 1), "(untagged)", sizeof ("(untagged)") - 1) != 0))){

            // name present, and isn't "(untagged)"
            // calculate the size of the symbol;
            usSymLen = sizeof (UDTSYM) + *Name;
            iPad = PAD4 (usSymLen);
            usNewLength = usSymLen + (ushort)iPad - LNGTHSZ;

            // allocate space for a new UDT Symbol

            NewSymbol = Alloc (usNewLength + LNGTHSZ + sizeof(char *));
            if (ExtraSymbols == NULL) {
                ExtraSymbols = NewSymbol;// head of list
            }
            else {
                // Change "next" field of the last sybol to point to the new one
                *ExtraSymbolLink = NewSymbol;
            }

            // Create the User Defined Type symbol
            pSym = (UDTPTR)(NewSymbol + sizeof(char *));
            DASSERT (usNewLength <= USHRT_MAX);
            pSym->reclen = (ushort)usNewLength;
            pSym->rectyp = S_UDT;
            pSym->typind = NewIndex;
            memcpy (pSym->name, Name, *Name + 1);  // Copy the name

            pchDest = ((uchar *)&(pSym->name)) + *Name + 1;
            PADLOOP (iPad, pchDest);

            // Store address of this symbol for next time
            ExtraSymbolLink = (uchar **)NewSymbol;
            // Set the "next" field of this symbol to NULL
            *ExtraSymbolLink = NULL;
        }

    }

    if ((NewIndex - CV_FIRST_NONPRIM) == LastGlobalTableIndex) {
        LastGlobalTableIndex += GTYPE_INC;
        pBuf = Alloc (GTYPE_INC * sizeof (uchar *));
        pGType[(NewIndex - CV_FIRST_NONPRIM) / GTYPE_INC] = pBuf;
    }

    length = C7LENGTH (pType) + LNGTHSZ;

#if DBG
    if (length % 2) {
        if (verbose) {
            printf("Leaf: 0x%X in module: %s is not a multiple of 2.  Rounding up.\n",
               pType->leaf, FormatMod(pCurMod));
        }
        length = (length + 1) & ~1;       // Ensure proper alignment
        pType->len = (pType->len + 1) & ~1;
    }
#endif
    if ( length > cbTypeAlign ) {
        // type exceeds demand load alignment limitation convert it to nil
        // sps 12/3/92
        *((ushort *)pType) = 2;     // Length of leaf
        *((ushort *)pType + 1) = LF_NULL;       // The leaf
        length = 4;
        Warn ( WARN_TYPELONG, NULL, NULL );
    }
    pBuf = pGType[(NewIndex - CV_FIRST_NONPRIM) / GTYPE_INC];
    pBuf[(NewIndex - CV_FIRST_NONPRIM) % GTYPE_INC] =
      VBufCpy (&TypeBuf, (uchar *)pType, length);
    FreeAllocStrings (OldEntry);
    OldEntry->TypeString = pBuf[(NewIndex - CV_FIRST_NONPRIM) % GTYPE_INC];
    OldEntry->CompactedIndex = NewIndex++;

    // we need to keep track of the following type records that were added
    // to the global types table.  The forward reference entries will
    // later be backpatched to the correct type.  We keep track of the
    // non-forward references so we know to ignore forward references
    // in later modules.

    HashFwdGlobal ((TYPPTR)(OldEntry->TypeString), (ushort)(NewIndex - 1));

    if (NewIndex == 0) {
#if DBG
        if (DbArray[3])
            DumpPartialType (NewIndex, (TYPPTR)(OldEntry->TypeString), FALSE);
#endif
        Warn (WARN_65KTYPES, FormatMod (pCurMod), NULL);
        NoMoTypes = TRUE;
    }
}


/**
 *
 *  AddTypeToStringTable
 *
 *  Adds a type string to the global hash table
 *  Uses C7 format type strings
 *
 */

void _fastcall AddTypeToStringTable (uchar *TypeString, CV_typ_t GlobalIndex)
{
    static uint      cStringsAvail;
    static HSSTRING *hstrAvail;

    ushort      i;
    HSSTRING   *j;

    if (cStringsAvail == 0) {
        hstrAvail = (HSSTRING *)Alloc(sizeof(HSSTRING) * 1024);
        cStringsAvail = 1024;
    }

    cStringsAvail--;
    j = hstrAvail++;

    i = StringHash (TypeString);

    j->TypeString = (TYPPTR) TypeString;
    j->CompactedIndex = GlobalIndex;
    j->Next = HPDBtring[i];
    HPDBtring[i] = j;
    HPDBtringCnt[i]++;
}


const char *PstGetLibPath(ushort iLib)
{
    const char *pstName = (char *) Libraries;

    for (;;) {
        size_t cbName;

        cbName = (size_t) *(unsigned char *) pstName;

        if (iLib-- == 0) {
            break;
        }

        pstName += cbName + 1;
    }

    return(pstName);
}


void GetCurModReferenceDir(char *szRefDir)
{
    const OMFModule *psstMod = (OMFModule *) ModAddr;
    const char *pstName;
    size_t cbName;
    char szPath[_MAX_PATH];
    char szDrive[_MAX_DRIVE];
    char szDir[_MAX_DIR];
    extern BYTE MajorLinkerVersion;
    extern BYTE MinorLinkerVersion;

    if (psstMod->iLib == 0) {
        pstName = (char *) &psstMod->SegInfo[psstMod->cSeg];
    } else if (!fLinearExe && (psstMod->iLib == 256)) {
        // Link 5.x mistakenly assigns 256 instead of 0

        pstName = (char *) &psstMod->SegInfo[psstMod->cSeg];
    } else if (fLinearExe && (MajorLinkerVersion == 2) && (MinorLinkerVersion < 60)) {
        // LINK 2.x before 2.60 mistakenly assign a value one too high

                pstName = PstGetLibPath((ushort) (psstMod->iLib - 1));
    } else {
        pstName = PstGetLibPath(psstMod->iLib);
    }

    cbName = (size_t) *(unsigned char *) pstName;

    memcpy(szPath, pstName + 1, cbName);
    szPath[cbName] = '\0';

    _splitpath(szPath, szDrive, szDir, NULL, NULL);
    _makepath(szRefDir, szDrive, szDir, NULL, NULL);

    if (szRefDir[0] == '\0') {
        // Never return an empty path

        szRefDir[0] = '.';
        szRefDir[1] = '\0';
    }
}


int TDBStayedResident;
int NeedToClearTDB;

LOCAL char szPDBCur[_MAX_PATH + _MAX_EXT];
LOCAL CV_typ_t  MaxIndexLast;                   // saved MaxIndex value

LOCAL EC        dummyEC;


LOCAL void ReadTDB(char* szPDBToRead)
{
    TI ti, tiMin, tiMac;
    uchar *pType;
    static char szPDBLast[_MAX_PATH + _MAX_EXT];

    if (szPDBToRead == NULL) {
        // if nothing to read - ropen the last one we read
        if (ppdb) {
            if (ptpi) {
                fAssert = TypesClose(ptpi);
            }

            fAssert |= PDBClose(ppdb);

            DASSERT(fAssert);
        }

        szPDBToRead = szPDBLast;

        if (!PDBOpen(szPDBToRead, pdbRead pdbGetRecordsOnly, 0, &dummyEC, NULL, &ppdb) ||
            !PDBOpenTpi(ppdb, pdbRead pdbGetRecordsOnly, &ptpi)) {
            ErrorExit(ERR_TDBOPEN, szPDBLast, NULL);
        }
    } else {
        _tcscpy(szPDBLast, szPDBToRead);
    }

    if (verbose) {
        printf(get_err(MSG_READPDB), szPDBCur, '\n');
    }

    // clear the whole local type table

    PostClearModNdx();

    // now install all the types from the TDB

    tiMin = TypesQueryTiMin(ptpi);
    tiMac = TypesQueryTiMac(ptpi);

    // printf("types: min = %d, max = %d\n", tiMin, tiMac);

    // loop through the indices...

    for (ti = tiMin; ti < tiMac; ti++) {
        fAssert = TypesQueryPbCVRecordForTi(ptpi, ti, &pType);
        DASSERT(fAssert);

#if DBG
    if (DbArray[9])
        DumpPartialType (ti, (TYPPTR) pType, FALSE);
#endif

        AddTypeEntry (pType, /* GTW: FALSE, */ TRUE, FALSE, T_NOTYPE, 0);
    }

    MaxIndexLast = MaxIndex;
}


/**     MapTDB - map in program database information
 *
 *      MapTDB (plf)
 *
 *      Entry   plf = pointer to LF_TDB type record up to name field
 *
 *      Exit    types from database inserted into this modules type entries
 *
 *      Returns none
 */

LOCAL void MapTDB(plfTypeServer plf)
{
    static char szPDBLast[_MAX_PATH];
    static ushort usPDB;
    BOOL fOpenPdb;
    char szPath[_MAX_PATH];
    char szExt[_MAX_EXT];
    char *pch;
    int ch;

    // copy and null terminate
    memcpy(szPDBCur, plf->name+1, plf->name[0]);
    szPDBCur[plf->name[0]] = 0;

    // check if there were no intervening initializations and if
    // the same database is being access as last time...

    if (usInitCount == usPDB + 1 && !_tcsicmp(szPDBLast, szPDBCur)) {
        DASSERT(ppdb);

        if (PDBQuerySignature(ppdb) != plf->signature) {
            ErrorExit(ERR_TDBSIG, FormatMod (pCurMod), szPDBCur);
        }

        if (PDBQueryAge(ppdb) < plf->age) {
            ErrorExit(ERR_TDBSYNC, FormatMod (pCurMod), szPDBCur);
        }

        // we're doing the same pdb as last time...
        // let's short circuit everything

        usPDB = usInitCount;
        MaxIndex = MaxIndexLast;
        TDBStayedResident = TRUE;

        if (PreviousMaxIndex > MaxIndex) {
            memset(ModNdxTab + MaxIndex, 0 ,
                (PreviousMaxIndex - MaxIndex) * sizeof(TENTRY));
        }

        PreviousMaxIndex = 0;

        return;
    }

    // Remember the last pdb name input not where we found it

    _tcscpy(szPDBLast, szPDBCur);
    usPDB = usInitCount;

    // we have to re-open the database just in case we have written
    // on the types that it returned last time...

    if (ppdb) {
        if (ptpi) {
            fAssert = TypesClose(ptpi);
        }

        fAssert |= PDBClose(ppdb);

        DASSERT(fAssert);
    }

    if (!PDBValidateInterface()) {
        ErrorExit(ERR_WRONGDBI, NULL, NULL);
    }

    // Try to open the PDB in the reference directory

    _splitpath(szPDBCur, NULL, NULL, szPath, szExt);
    _tcscat(szPath, szExt);

    GetCurModReferenceDir(szPDBCur);

    pch = szPDBCur + _tcslen(szPDBCur);

    if (((ch = pch[-1]) != '/') && (ch != '\\') && (ch != ':')) {
        // Add a trailing '\'

        *pch++ = '\\';
    }

    _tcscpy(pch, szPath);

    fOpenPdb = PDBOpen(szPDBCur, pdbRead pdbGetRecordsOnly, 0, &dummyEC, NULL, &ppdb);

    if (fOpenPdb && (PDBQuerySignature(ppdb) != plf->signature)) {
        PDBClose(ppdb);

        fOpenPdb = FALSE;
    }

#if 0
    if (!fOpenPdb) {
        char LibPath[4 * _MAX_PATH];
        char *plibenv;

        // look along lib path first - this was done to prevent troubles profiling
        // staticly linked mfc apps - which are built on e:

        plibenv = getenv("LIB");
        if (plibenv == NULL) {
            plibenv = "";
        }

        _tcscpy(LibPath, ".;");
        _tcscat(LibPath, plibenv);

        plibenv = LibPath;

        while (!fOpenPdb) {
            char *plibsemi;

            if (*plibenv == 0) {
                break;
            }

            // Try to open the file along the next directory in the LIB path

            if ((plibsemi = _tcschr(plibenv, ';')) != NULL) {
                *plibsemi = 0;
                _tcscpy(szPDBCur, plibenv);
                plibenv = plibsemi + 1;
            } else {
                _tcscpy(szPDBCur, plibenv);
                *plibenv = 0;
            }

            pch = szPDBCur + _tcslen(szPDBCur);

            if (((ch = pch[-1]) != '/') && (ch != '\\') && (ch != ':')) {
                // Add a trailing '\'

                *pch++ = '\\';
            }

            _tcscpy(pch, szPath);

            fOpenPdb = PDBOpen(szPDBCur, pdbRead pdbGetRecordsOnly, 0, &dummyEC, NULL, &ppdb);

            if (fOpenPdb && (PDBQuerySignature(ppdb) != plf->signature)) {
                PDBClose(ppdb);

                fOpenPdb = FALSE;
            }
        }
    }
#endif

    if (!fOpenPdb) {
        // Try to open the PDB using the given location

        _tcscpy(szPDBCur, szPDBLast);

        fOpenPdb = PDBOpen(szPDBCur, pdbRead pdbGetRecordsOnly, 0, &dummyEC, NULL, &ppdb);

        if (fOpenPdb && (PDBQuerySignature(ppdb) != plf->signature)) {
            PDBClose(ppdb);

            fOpenPdb = FALSE;
        }
    }

    if (!fOpenPdb) {
        ErrorExit(ERR_TDBOPEN, szPDBCur, NULL);
    }

    if (PDBQueryAge(ppdb) < plf->age) {
        ErrorExit(ERR_TDBSYNC, FormatMod(pCurMod), szPDBCur);
    }

    if (!PDBOpenTpi(ppdb, pdbRead pdbGetRecordsOnly, &ptpi)) {
        ErrorExit(ERR_TDBOPEN, szPDBCur, NULL);
    }

    ReadTDB(szPDBCur);
}


/**
 *
 *  AddTypeToTypeTable
 *
 *  Add a type string to the type table for recursive types
 *  Uses C7 format type strings
 *
 */

void _fastcall AddTypeToTypeTable (TENTRY *OldEntry)
{
    static    uint   cTypesAvail;
    static    HSTYPE *hstAvail;

    HSTYPE    *new;
    int        index;

    if (cTypesAvail == 0) {
        hstAvail = (HSTYPE *)Alloc(sizeof(HSTYPE) * 1024);
        cTypesAvail = 1024;
    }

    index = TypeHash (OldEntry);

    cTypesAvail--;
    new = hstAvail++;
    new->Type = OldEntry->TypeString;
    new->GlobalIndex = OldEntry->CompactedIndex;

    // add it to the head of the bucket
    new->Next = HTType[index];
    HTType[index] = new;
    HTTypeCnt[index]++;
}


/**     C7ReadTypes - read C7 formatted types table
 *
 *      C7ReadTypes (cbTypes, fPreComp)
 *
 *      Entry   cbTypes = count of bytes in types table excluding the
 *              byte count of the signature
 *              fPreComp = TRUE if precompiled types table allowed
 *
 */

void C7ReadTypes (ulong cbTypeSeg, bool_t fPreComp)
{
    ulong       cbCur;          // Number of bytes currently read from file
    ushort      cbRecLen;       // The current record's length
    ushort      RecType;        // The current record's length
    uchar      *pCurType;       // Points to the current type string
    uchar      *pEnd;           // Points to the of type segment
    int         remainder = 0;
    ushort      cSkip;
    uint        cbRead;

    InitModTypeTable ();
    cbCur = 0;
    iTypeSeg = 0;
    while (cbCur < cbTypeSeg) {
        pCurType = pTypeSeg[iTypeSeg];
        cbRead = (uint)(min (cbTypeSeg - cbCur, _HEAP_MAXREQ)) - remainder;
        if (cbRead == 0)
            cbRead = remainder;
//printf("cbRead %u\tRemain: %u\t", cbRead, remainder);

        if (link_read (exefile, pCurType + remainder, cbRead) != cbRead) {
            ErrorExit (ERR_INVALIDTABLE, "Types", FormatMod (pCurMod));
        }
        pEnd = pCurType + cbRead + remainder;
//printf("pEnd: %d\tpCur: %d\n", pEnd, pCurType);
        while ((size_t)(pEnd - pCurType) >= offsetof (TYPTYPE, data)) {
            // we have at least the length and record type in memory

            cbRecLen = ((TYPPTR)pCurType)->len;
            RecType = ((TYPPTR)pCurType)->leaf;

            if ((size_t)(pEnd - pCurType) <
              (cbRecLen + sizeof (((TYPPTR)pCurType)->len))) {
                // the type record is split across this and the next buffer

                break;
            }

            switch (RecType) {
                case LF_TYPESERVER:
#pragma message("M00TODO - remove when compiler doesn't mark tdb as pct")
                    MapTDB((plfTypeServer) (pCurType + LNGTHSZ));
                    // REVIEW -- workaround!  [rm]
                    // compiler must never mark a TDB as a PCT
                    // REMOVE THIS WHEN COMPILER FIXED
                    maxPreComp = MaxIndex + CV_FIRST_NONPRIM;
                    break;

                case LF_PRECOMP:
                    if(fPreComp)
                        ErrorExit (ERR_2LevelPch, FormatMod (pCurMod), 0);

                    MapPreComp ((plfPreComp)(pCurType + LNGTHSZ));
                    break;

                case LF_ENDPRECOMP:
                    // we have found the type record that specifies
                    // the end of the precompiled types for this module.
                    // Set maxPreComp to MaxIndex (really current index)
                    // so the precompiled types packer knows how far
                    // to pack before the publics and symbols are packed.

                    DASSERT(fPreComp);
                    maxPreComp = MaxIndex + CV_FIRST_NONPRIM;
#pragma inline_depth(0)     // don't inline this occurence
                    AddTypeEntry (NULL, TRUE, FALSE, T_NOTYPE, 0);
#pragma inline_depth()
                    pCurMod->signature =
                      ((plfEndPreComp)(pCurType + LNGTHSZ))->signature;
                    break;

                default:
                    if (RecType == LF_SKIP) {
                        cSkip = ((plfSkip)(pCurType + LNGTHSZ))->type -
                          MaxIndex - CV_FIRST_NONPRIM;
                    }
                    else {
                        cSkip = 0;
                    }
#if DBG
                    if ((ULONG) pCurType % 2) {
                        uchar *pTypeNew;
                        if (verbose) {
                            printf("Leaf: 0x%X in module: %s doens't start on a word boundary.\n",
                                ((TYPTYPE UNALIGNED *)pCurType)->leaf, FormatMod(pCurMod));
                        }
                        pTypeNew = CAlloc((((TYPTYPE UNALIGNED *)pCurType)->len+4+3)& ~3);
                        memcpy(pTypeNew, pCurType, ((TYPTYPE UNALIGNED *)pCurType)->len + 4);
                        AddTypeEntry(pTypeNew, TRUE, FALSE, T_NOTYPE, cSkip);
                    }
                    else
#endif
                        AddTypeEntry (pCurType, TRUE, FALSE, T_NOTYPE, cSkip);
                    break;
            }
            pCurType += cbRecLen + LNGTHSZ;
            cbCur += cbRecLen + LNGTHSZ;
        }

        // we have reached the point where we are either at the end of types
        // or the next type record does not fit within the current buffer

//printf("cbCur: %d\tcbType: %d\n", cbCur, cbTypeSeg);

        if (cbCur < cbTypeSeg) {
           // we have not reached the end of the types so we allocate and
           // read another buffer

           // Range check (ReadDir s/b correct here)
           if (iTypeSeg > cTypeSeg) {
               ErrorExit (ERR_TYPE, FormatMod(pCurMod), NULL);
           }

           if (pTypeSeg[iTypeSeg + 1] == NULL) {
               if ((pTypeSeg[iTypeSeg + 1] = TrapMalloc (_HEAP_MAXREQ)) == 0) {
                   ErrorExit (ERR_NOMEM, NULL, NULL);
               }
           }
           remainder = pEnd - pCurType;
           memmove (pTypeSeg[iTypeSeg + 1], pCurType, remainder);
           iTypeSeg++;
        }
    }
    InitialTypeInfoSize += cbTypeSeg;   // update size info
}








/**     C6ReadTypes - read C6 types table
 *
 *  Purpose : Given a Type segment and its size, initialize the
 *        module type index table and set all global indices to
 *        zero
 */

void C6ReadTypes (uchar *TypeSegment, ulong Size)
{
    uchar     *End = TypeSegment + Size;
    ushort      cSkip;

    InitModTypeTable ();
    while (TypeSegment < End) {
        if (TypeSegment[3] == OLF_SKIP) {
            cSkip = (*(CV_typ_t *)(TypeSegment + 4)) - MaxIndex - 512;
        }
        else {
            cSkip = 0;
        }
#pragma inline_depth(0)     // don't inline this occurence
        AddTypeEntry (TypeSegment, FALSE, FALSE, T_NOTYPE, cSkip);
#pragma inline_depth()
        if (TypeSegment[3] == OLF_STRUCTURE) {
            // We have to add a symbol for each C6 UDT found.  We
            // are deliberately overestimating the size of the symbol
            // because it takes too much time to do an accurate estimate

            UDTAdd += MAXPAD + sizeof (UDTSYM) + *(ushort UNALIGNED *)&TypeSegment[1];
        }
        TypeSegment += LENGTH (TypeSegment) + 3; // on to next string
    }
    InitialTypeInfoSize += (unsigned long) Size;    // update size info
}




/**     InitModTypeTable - initialize/reset module type table
 *
 *      InitModTypeTable ()
 *
 *      Entry   BlockList = head of first level index structure
 *
 *      Exit    cur = pointer to first level index structure
 *              MaxIndex = 0
 *              if BlockList = NULL, then a first level index structure is
 *              allocated and initialized.  If BlockList is not NULL, then
 *              the first level index structure is initialized.
 */


void InitModTypeTable (void)
{
    usInitCount++;

    if (ModNdxTab == NULL) {
        ModNdxTab = CAlloc((SizeModNdxTab = INITMODTABSIZE) * sizeof(TENTRY));
    }
    MaxIndex = 0;

}



// ClearHashFwdLocal - clear out the hash table for forward reference resolution

void ClearHashFwdLocal() {
    HSFWD      *pHash;
    uint        i;

    for (i = 0; i < HASH_FWD; i++) {
        pHash = HTLocalFwd[i];
        while (pHash != NULL) {
            pHash->index = T_NOTYPE;
            pHash->pType = NULL;
            free (pHash->pName);
            pHash->pName = NULL;
            pHash = pHash->Next;
        }
    }
}

/**     AddTypeEntry - add type descriptor to local table
 *
 *      AddTypeEntry (pType, IsNewFormat, IsPrecomp, type, cSkip)
 *
 *      Entry   pType = pointer to type string
 *              IsNewFormat = TRUE if type is CV4 format
 *              IsPreComp = TRUE if precompile type from another module
 *              cSkip = number of indices to skip before adding this type
 *
 *      Exit    type added to local type descriptors
 *
 *      Return  none
 *
 *      Note:   MapPreComp contains an inlined/simplified version of this
 *              function, if AddTypeEntry ever changes semantics then
 *              the inline code in MapPreComp should be examined [rm]
 *
 */


INLINE void _fastcall
AddTypeEntry (
    uchar *pType,
    bool_t IsNewFormat,
    bool_t IsPreComp,
    CV_typ_t type,
    ushort cSkip
    )
{
    CV_typ_t insertHere = MaxIndex;

    PostClearModNdx();

    if ((MaxIndex += (cSkip) ? cSkip : 1) >= SizeModNdxTab) {
        if ((ModNdxTab = realloc(ModNdxTab,
            (SizeModNdxTab += REALLOCMODTABSIZE + cSkip) * sizeof(TENTRY))) == NULL){
            ErrorExit(ERR_NOMEM, NULL, NULL);
            }
        memset(ModNdxTab + insertHere, 0,(REALLOCMODTABSIZE + cSkip) * sizeof(TENTRY));
    }

    ModNdxTab[insertHere].TypeString = pType;
    ModNdxTab[insertHere].CompactedIndex = type;
    DASSERT(ModNdxTab[insertHere].flags.IsMalloced == FALSE);
    ModNdxTab[insertHere].flags.IsNewFormat = IsNewFormat;
    ModNdxTab[insertHere].flags.IsPreComp = IsPreComp;
    ModNdxTab[insertHere].flags.InputWasFwd = IsFwdRef ((TYPPTR) pType);
}


/**     MapPreComp - map in precompiled types from creator module
 *
 *      MapPreComp (plf)
 *
 *      Entry   plf = pointer to LF_PRECOMP type record up to name field
 *
 *      Exit    types from creator file inserted into this modules type entries
 *
 *      Returns none
 */


LOCAL void MapPreComp (plfPreComp plf)
{
    PMOD            pMod;
    ushort          j;
    OMFPreCompMap  *pMap;

    static PMOD     pModLast;                   // last module we installed
    static ushort usPreCompCount;
    static CV_typ_t MaxIndexLast;               // saved MaxIndex value

    pMod = FindModule ((char *)&plf->name);
    if ((pMap = (OMFPreCompMap *) pMod->PreCompAddr) == NULL) {
        ErrorExit (ERR_NOMEM, NULL, NULL);
    }
    if (pMod->signature != plf->signature) {
        ErrorExit (ERR_PCTSIG, FormatMod (pCurMod), FormatMod(pMod));
    }
    if ((pMap->cTypes != plf->count) ||
      (MaxIndex + CV_FIRST_NONPRIM != plf->start)) {
        ErrorExit (ERR_PRECOMPERR, FormatMod (pCurMod), FormatMod (pMod));
    }
    if (usInitCount == usPreCompCount + 1 && pMod == pModLast) {
        // we're doing the same module as last time...
        // let's short circuit everything

        usPreCompCount = usInitCount;
        MaxIndex    = MaxIndexLast;
        if (PreviousMaxIndex > MaxIndex) {
            memset(ModNdxTab + MaxIndex, 0 , (PreviousMaxIndex - MaxIndex) * sizeof(TENTRY));
        }
        PreviousMaxIndex = 0;
        return;
    }

    PostClearModNdx();

    for (j = 0; j < plf->count; j++) {
        ModNdxTab[MaxIndex].CompactedIndex = pMap->map[j];
        ModNdxTab[MaxIndex].flags.IsNewFormat = TRUE;
        ModNdxTab[MaxIndex++].flags.IsPreComp = TRUE;
    }
    usPreCompCount  = usInitCount;
    MaxIndexLast    = MaxIndex;
    pModLast        = pMod;

}


LOCAL PMOD FindModule (char *pName)
{
    PMOD        pMod = ModuleList;
    char        Name[257];

    // search to end of module list

    while (pMod != NULL) {
        if ((pMod->pName != NULL) && (*pName == *pMod->pName)) {
            if (_tcsnicmp(pName + 1, pMod->pName + 1, (size_t)*pName) == 0) {
                return (pMod);
            }
        }
        pMod = pMod->next;
    }
    _tcsncpy(Name, pName + 1, (size_t)*pName);
    Name[*pName] = 0;
    ErrorExit (ERR_REFPRECOMP, Name, NULL);
}

COUNTER (cnt_GetTypeEntry);
COUNTER (cnt_GetTypeEntry2);


/**     GetRecursiveIndex -
 *
 *      Given a local index to a recursive structure, consults the
 *      hash table to see if another similar structure is present
 *      or not. If yes, return the global index, else insert the
 *      structure into the compacted segment and return its index
 *
 */

COUNTER (cnt_GetRecursiveIndex);


CV_typ_t GetRecursiveIndex (TENTRY *OldEntry, CV_typ_t OldIndex)
{
    HSTYPE     *j;
    TYPPTR      pType;

    COUNT (cnt_GetRecursiveIndex);
    DASSERT (OldIndex >= usCurFirstNonPrim);
    DASSERT (MaxIndex > OldIndex - usCurFirstNonPrim);
    pType = (TYPPTR)OldEntry->TypeString;
    DASSERT(OldEntry->Count != 0);

    j = HTType[TypeHash (OldEntry)];
    while (j != NULL) {
        if (IdenticalTree (OldEntry, OldIndex, (TYPPTR)j->Type,
          j->GlobalIndex)) {
            return (j->GlobalIndex);
        }
        else {
            j = j->Next;
        }
    }
    return (AddRecursiveType (OldIndex));
}




/**     FindFwdRef - find definition of forward reference
 *
 *
 *      state = FindFwdRef (OldEntry, HSFWD **ppHash, fLocal)
 *
 *      Entry   OldEntry = pointer to index structure
 *              fLocal = TRUE of local table to be searched
 *
 *      Exit    **ppHash = pointer to hash entry
 *
 *      Return  FWD_none if definition not found
 *              FWD_local if definition found in local table
 *              FWD_global if definition found in global table
 *              FWD_globalfwd if forward reference found in global table
 */

FWD_t _fastcall FindFwdRef (TENTRY *OldEntry, HSFWD **ppHash, bool_t fLocal)
{
    uchar      *pName;
    TYPPTR      pType = (TYPPTR)OldEntry->TypeString;
    FWD_t       retval = FWD_none;

    switch (pType->leaf) {
        case LF_CLASS:
        case LF_STRUCTURE:
            pName = (uchar *)&((plfClass)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_UNION:
            pName = (uchar *)&((plfUnion)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_ENUM:
            pName = (uchar *)&((plfEnum)&(pType->leaf))->Name;
            break;

        default:
            return (retval);
    }
    if (fLocal) {
        if ((retval = FindLocalFwd (pType->leaf, pName, ppHash, IsUdtNested(pType))) != FWD_local) {
            retval = FindGlobalFwd (pType->leaf, pName, ppHash);
        }
    }
    else {
        retval = FindGlobalFwd (pType->leaf, pName, ppHash);
    }
    return (retval);
}




/**     FindLocalFwd - find local definition of forward reference
 *
 *      status = FindLocalFwd (leaf, pName, ppHash)
 *
 *      Entry   leaf = leaf type
 *              pName = pointer to name string
 *              pIndex = pointer to replacement index
 *
 *      Exit    **ppHash = pointer to hash entry
 *
 *      Return  FWD_none if definition not found
 *              FWD_local if definition found in local table
 */

COUNTER (cnt_FindLocalFwd);

LOCAL FWD_t FindLocalFwd (ushort leaf, char *pName, HSFWD **ppHash, bool_t isNested)
{
    uchar   *pc;
    uint    Sum;
    uint    i;
    uint    hash;
    HSFWD  *pHash;

    COUNT (cnt_FindLocalFwd);
    pc = pName;
    Sum = *pc;
    for (i = *pc++; i > 0; i--) {
        Sum += *pc++;
    }
    hash = Sum % HASH_FWD;
    pHash = HTLocalFwd[hash];
    while ((pHash != NULL) && (pHash->pType != 0)) {
        DASSERT (pHash->index >= CV_FIRST_NONPRIM);
        if ((leaf == pHash->pType->leaf) &&
            (pHash->pName[0] == pName[0]) &&
            (memcmp(pHash->pName+1, pName+1, pName[0]) == 0) &&
            (isNested == IsUdtNested(pHash->pType))) {
            *ppHash = pHash;
            return(FWD_local);
        }
        pHash = pHash->Next;
    }
    return (FWD_none);
}




/**     FindGlobalFwd - find global definition of forward reference
 *
 *      status = FindGlobalFwd (leaf, pName, pIndex)
 *
 *      Entry   leaf = leaf type
 *              pName = pointer to name string
 *              pIndex = pointer to replacement index
 *
 *      Exit    *pIndex = replacement index
 *
 *      Return  FWD_none if definition not found
 *              FWD_global if definition found in global table
 *              FWD_globalfwd if forward reference found in global table
 */

COUNTER (cnt_FindGlobalFwd);

LOCAL FWD_t FindGlobalFwd (ushort leaf, char *pName, HSFWD **ppHash)
{
    uchar      *pc;
    uint        Sum;
    uint        i;
    uint        hash;
    HSFWD      *pHash;
    uchar     **pBuf;
    FWD_t       retval = FWD_none;
    TYPPTR      pType;

    COUNT (cnt_FindGlobalFwd);
    pc = pName;
    Sum = *pc;
    for (i = *pc++; i > 0; i--) {
        Sum += *pc++;

    }
    hash = Sum % HASH_FWD;
    pHash = HTGlobalFwd[hash];
    while ((pHash != NULL) && (pHash->pType != 0)) {
        DASSERT (pHash->index >= CV_FIRST_NONPRIM);
        if ((leaf == pHash->pType->leaf) &&
            (pHash->pName[0] == pName[0]) &&
            (memcmp(pHash->pName+1, pName+1, pName[0]) == 0)) {
            // the names and record types are identical.  we now need
            // to check to see if this is a forward reference or a
            // definition

            pBuf = pGType[(pHash->index - CV_FIRST_NONPRIM) / GTYPE_INC];
            pType = (TYPPTR)pBuf[(pHash->index - CV_FIRST_NONPRIM) %
              GTYPE_INC];
            *ppHash = pHash;
            return(IsFwdRef(pHash->pType) ? FWD_globalfwd : FWD_global);
        }
        pHash = pHash->Next;
    }
    return (FWD_none);
}


/**     HashFwdGlobal - hash type strings for forward ref resolution
 *
 *      HashFwdGlobal (pType, pTable)
 *
 *      Entry   pType = pointer to type string
 *              pTable = hash table to add string to
 *
 *      Exit    type string hashed into table
 *
 *      Returns none
 */

COUNTER (cnt_HashFwd);

INLINE void HashFwdGlobal (TYPPTR pType, CV_typ_t index)
{
    uchar   *pName;
    uchar   *pc;
    uint    Sum;
    uint    hash;
    uint    i;
    HSFWD  *pHash;

    COUNT (cnt_HashFwd);
    switch (pType->leaf) {
        case LF_CLASS:
        case LF_STRUCTURE:
            pName = (uchar *)&((plfClass)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_UNION:
            pName = (uchar *)&((plfUnion)&(pType->leaf))->data;
            pName += C7SizeOfNumeric (pName);
            break;

        case LF_ENUM:
            pName = (uchar *)&((plfEnum)&(pType->leaf))->Name;
            break;

        default:
            // don't care about any of these just return;
            return;
    }
    pc = pName;
    Sum = *pc;
    for (i = *pc++; i > 0; i--) {
        Sum += *pc++;

    }
    DASSERT (index >= CV_FIRST_NONPRIM);
    hash = Sum % HASH_FWD;
    pHash = HTGlobalFwd[hash];
    while ((pHash != NULL) && (pHash->pType != 0)) {
        DASSERT (pHash->index >= CV_FIRST_NONPRIM);
        if ((pType->leaf == pHash->pType->leaf) &&
            (pHash->pName[0] == pName[0]) &&
            (memcmp(pHash->pName+1, pName+1, pName[0]) == 0)) {
            return;
        }
        pHash = pHash->Next;
    }
    if (pHash == NULL) {
        // add new entry to table since we are at then end
        if ((pHash = (HSFWD *) CAlloc(sizeof (HSFWD))) == NULL) {
            ErrorExit(ERR_NOMEM, NULL, NULL);
        }
        pHash->Next = HTGlobalFwd[hash];
        HTGlobalFwd[hash] = pHash;
    }
    pHash->pType = pType;
    pHash->pName = pName;
    pHash->index = index;
}




/**     SumUCChar - generate sum of upper case character hash
 *
 *      hash = SumUCChar (pSym, pSum, pLen, modulo)
 *
 *      Entry   pSym = pointer to symbol
 *              pSum = pointer to sum
 *              pLen = pointer of offset of length prefixed name
 *              modulo = hash table size
 *
 *      Exit    *sum = sum of characters (upper cased) in name
 *
 *      Returns *sum / modulo
 */


#define dwrd_toupper(dw) (dw & 0xDFDFDFDF)
#define byt_toupper(b) (b & 0xDF)

ushort NEAR DWordXorShift (
    SYMPTR pSym,
    ulong  *pSum,
    ushort *pLen,
    ushort *pSeg,
    ulong  *pOff,
    ushort modulo
) {
    uchar      *pbName;
    ulong UNALIGNED *pulName;
    int         cb;
    int         cul;
    int         iul;
    ulong       ulSum = 0;
    ulong       ulEnd = 0;
    ushort      seg   = 0;
    ulong       off   = 0;

    switch (pSym->rectyp) {

        case S_CONSTANT:
            pbName = (uchar *)(&((CONSTPTR)pSym)->value);
            pbName += C7SizeOfNumeric (pbName);
            break;

        case S_GDATA16:
        case S_LDATA16:
        case S_PUB16:       // Note that the structure pub16==data16
            pbName = (uchar *)(&((DATAPTR16)pSym)->name);
            seg = ((DATAPTR16)pSym)->seg;
            off = ((DATAPTR16)pSym)->off;
            break;

        case S_GDATA32:
        case S_LDATA32:
        case S_PUB32:
        case S_GTHREAD32:
        case S_LTHREAD32:   // Note that the structure pub32==data32==thread32
            pbName = (uchar *)(&((DATAPTR32)pSym)->name);
            seg = ((DATAPTR32)pSym)->seg;
            off = ((DATAPTR32)pSym)->off;
            break;

        case S_UDT:
            pbName = (uchar *)(&((UDTPTR)pSym)->name);
            break;

        case S_GPROC16:
        case S_LPROC16:
            pbName = (uchar *)(&((PROCPTR16)pSym)->name);
            seg = ((PROCPTR16)pSym)->seg;
            off = ((PROCPTR16)pSym)->off;
            break;

        case S_GPROC32:
        case S_LPROC32:
            pbName = (uchar *)(&((PROCPTR32)pSym)->name);
            seg = ((PROCPTR32)pSym)->seg;
            off = ((PROCPTR32)pSym)->off;
            break;

        case S_GPROCMIPS:
        case S_LPROCMIPS:
            pbName = (uchar *)(&((PROCSYMMIPS *)pSym)->name);
            seg = ((PROCSYMMIPS *)pSym)->seg;
            off = ((PROCSYMMIPS *)pSym)->off;
            break;

        default:
            DASSERT ( FALSE );

            *pSeg = 0;
            *pOff = 0;
            *pSum = 0;
            return (0);
    }
    *pLen = pbName - (uchar *)pSym;
    cb = *pbName++;
    pulName = (ulong UNALIGNED *) pbName;

    while ( cb & 3 ) {
        ulEnd |= byt_toupper ( pbName [ cb - 1 ] );
        ulEnd <<= 8;
        cb -= 1;
    }

    cul = cb / 4;

    for ( iul = 0; iul < cul; iul++ ) {
        ulSum ^= dwrd_toupper(pulName[iul]);
        ulSum  = _lrotl ( ulSum, 4 );
    }
    ulSum ^= ulEnd;

    *pSeg = seg;
    *pOff = off;
    *pSum = ulSum;
    return (ushort) (ulSum % modulo);
}


/**     PrepareGlobalTypeTable
 *
 *      Entry   TypeBuf - Linked list of blocks containing Global types.
 *                        The global type strings are unpadded.
 *
 *      Exit    GlobalTypeTable contains an array of offsets to the types.
 *              These offsets are calculated on the assumption that at write
 *              time the strings will be placed on 4 word bounderies.
 *
 */

void PrepareGlobalTypeTable ()
{
    ushort      usTotal;        // Length of record including size of length field
    ushort      i;
    uchar      *Types;
    ulong      *pBuf;
    ulong       Offset = 0;
    ushort      cb = 0;

    for (i = 0; i < (CV_typ_t)(NewIndex - (CV_typ_t)CV_FIRST_NONPRIM); i++) {
        pBuf = (ulong *)pGType[i / GTYPE_INC];
        Types = (uchar *) pBuf[i % GTYPE_INC];

        // Get the length of this record

        usTotal = ((SYMPTR)Types)->reclen + LNGTHSZ;
        DASSERT( usTotal <= cbTypeAlign );
        if ( cb + usTotal + PAD4 ( usTotal ) > cbTypeAlign ) {
            Offset += cbTypeAlign - cb;
            cb = 0;
        }
        cb += usTotal + PAD4 ( usTotal );

        // Set offset in table

        pBuf[i % GTYPE_INC] = Offset;

        // Move to the next type

        Types += usTotal;

        // Calculate the address of the next type string after padding

        Offset += usTotal + PAD4 (usTotal);
    }

    DASSERT (i == (ushort)(NewIndex - CV_FIRST_NONPRIM));
}




/**     PackPublic - pack public symbol into list if possible
 *
 *      flag = PackPublic (pSym, lpfnHash)
 *
 *      Entry   pSym = pointer to symbol
 *              lpfnHash = pointer to hash function
 *
 *      Exit    symbol added to global symbol table if possible
 *              original symbol type changed to S_CVRESERVE if packed
 *
 *      Returns GPS_intable if symbol is in the global symbol table
 *              GPS_added if symbol can be added to the global table
 *              GPS_noadd if symbol of same name but different type in table
 *
 */


GPS_t PackPublic (SYMPTR pSym, LPFNHASH lpfnHash)
{
    uchar      *pName;
    uchar      *pTemp;
    ushort      hashName;
    ushort      indName;
    GLOBALSYM  *p;
    GLOBALSYM  *pNew;
    ushort      len;
    ushort      iPad;
    uint        cbReqd;
    ulong       sumName;
    ushort      seg = 0;
    ulong       uoff = 0;

    iPad = PAD4 (pSym->reclen + LNGTHSZ);

    hashName = lpfnHash (
        (SYMPTR)pSym,
        &sumName,
        &indName,
        &seg,
        &uoff,
        HASH_SYM
    );

    pName = (uchar *)pSym + indName;
    len = *pName;
    for (p = HTPub[hashName]; p != NULL; p = p->Next) {
        pTemp = p->Sym + p->indName;
        if (sumName == p->sumName) {
            if (memcmp(pName, pTemp, len + 1) == 0) {

                // name is in global symbol table.  Now make sure the
                // symbol record is identical.
                PCHAR arg1 = (PCHAR) (((PCHAR) pSym) + LNGTHSZ);
                PCHAR arg2 = (PCHAR) (((PCHAR) p->Sym) + LNGTHSZ);
                // Add the padding (otherwise, length may not match).
                int Length = (int) (pSym->reclen - p->indName - LNGTHSZ + iPad);
                if (memcmp (arg1, arg2, Length) == 0) {
                    pSym->rectyp = S_CVRESERVE;
                    cGlobalDel++;
                    return (GPS_intable);
                }
                else {
                    // the name is in the table, but the record is different
                    PCHAR SymbolName = calloc(1, len+1);
                    memcpy(SymbolName, pName+1, len);
                    Warn (WARN_DUPPUBLIC, SymbolName, FormatMod (pCurMod));
                    free(SymbolName);
                    return (GPS_noadd);
                }
            }
        }
    }

    cbReqd = (sizeof (GLOBALSYM) + pSym->reclen + LNGTHSZ + iPad);

    pNew = AllocForHT ( cbReqd );

    pNew->sumName = sumName;
    pNew->indName = indName;
    pNew->seg   = seg;
    pNew->uoff  = uoff;
    memcpy (pNew->Sym, pSym, pSym->reclen + LNGTHSZ);
    pTemp = pNew->Sym + pSym->reclen + LNGTHSZ;
    ((SYMPTR)&(pNew->Sym))->reclen += iPad;
    cbPublics += pSym->reclen + LNGTHSZ + iPad;
    cPublics++;
    PADLOOP (iPad, pTemp);
    pNew->Next = HTPub[hashName];
    HTPub[hashName] = pNew;
    pSym->rectyp = S_CVRESERVE;
    return (GPS_added);
}



/**     PackSymbol - pack symbol into global symbol list if possible
 *
 *      flag = PackSymbol (pSym, lpfnHash)
 *
 *      Entry   pSym = pointer to symbol
 *              lpfnHash = pointer to hash function
 *
 *      Exit    symbol added to global symbol table if possible
 *              original symbol type changed to S_CVRESERVE if packed
 *
 *      Returns GPS_intable if symbol is in the global symbol table
 *              GPS_added if symbol can be added to the global table
 *              GPS_noadd if symbol of same name but different type in table
 *
 */

GPS_t _fastcall PackSymbol (SYMPTR pSym, LPFNHASH lpfnHash)
{
    uchar      *pName;
    uchar      *pTemp;
    ushort      hashName;
    ushort      indName;
    GLOBALSYM  *p;
    GLOBALSYM  *pNew;
    ushort      len;
    ushort      iPad;
    uint        cbReqd;
    ulong       sumName;
    ushort      seg  = 0;
    ulong       uoff = 0;

    hashName = lpfnHash (
        (SYMPTR)pSym,
        &sumName,
        &indName,
        &seg,
        &uoff,
        HASH_SYM
    );

    pName = (uchar *)pSym + indName;
    len = *pName;
    for (p = HPDBym[hashName]; p != NULL; p = p->Next) {
        pTemp = p->Sym + p->indName;
        if (sumName == p->sumName) {
            if (memcmp (pName, pTemp, len + 1) == 0) {

                // name is in global symbol table.  Now make sure the
                // symbol record is identical.

                if (memcmp ((uchar *)pSym + LNGTHSZ, (uchar *)(&p->Sym) + LNGTHSZ,
                  pSym->reclen) == 0) {
                    pSym->rectyp = S_CVRESERVE;
                    cGlobalDel++;
                    return (GPS_intable);
                }
                else {
                    // the name is in the table, but the record is different
                    return (GPS_noadd);
                }
            }
        }
    }
    iPad = PAD4 (pSym->reclen + LNGTHSZ);

    cbReqd = sizeof (GLOBALSYM) + pSym->reclen + LNGTHSZ + iPad;

    pNew = AllocForHT ( cbReqd );

    pNew->sumName = sumName;
    pNew->indName = indName;
    pNew->seg     = seg;
    pNew->uoff    = uoff;
    memcpy (pNew->Sym, pSym, pSym->reclen + LNGTHSZ);
    pTemp = pNew->Sym + pSym->reclen + LNGTHSZ;
    ((SYMPTR)&(pNew->Sym))->reclen += iPad;
    cbGlobalSym += pSym->reclen + LNGTHSZ + iPad;
    cGlobalSym++;
    PADLOOP (iPad, pTemp);
    pNew->Next = HPDBym[hashName];
    HPDBym[hashName] = pNew;
    pSym->rectyp = S_CVRESERVE;
    return (GPS_added);
}

void WritePublics (OMFDirEntry *Dir, long lfoStart)
{

    OMFSymHash  hash;
    GLOBALSYM   *p;
    VBuf        NameHashBuf;
    VBlock     *NameHashBlock;
    VBuf        AddrHashBuf;
    VBlock     *AddrHashBlock;
    ushort      iHash;
    int         PadCount;
    ulong       Zero = 0;

    memset ( &hash, 0, sizeof (hash) );

    cbPublics += AlignTable ( HTPub, sizeof ( long ) );

    hash.cbSymbol = cbPublics;
    BuildHash (
        &HASHFUNC,
        &hash.symhash,
        &hash.cbHSym,
        &NameHashBuf,
        cPublics,
        HTPub
    );

    BuildSort (
        &hash.addrhash,
        &hash.cbHAddr,
        &AddrHashBuf,
        cPublics,
        HTPub
    );

    Dir->SubSection = sstGlobalPub;
    Dir->iMod = 0xffff;
    Dir->lfo = lfoStart;
    Dir->cb = sizeof (OMFSymHash) + hash.cbSymbol + hash.cbHSym + hash.cbHAddr;

    // write out global symbols

    if (!BWrite ((char *)&hash, sizeof (hash))) {
        ErrorExit (ERR_NOSPACE, NULL, NULL);
    }
    for (iHash = 0; iHash < HASH_SYM; iHash++) {
        for (p = HTPub[iHash]; p != NULL; p = p->Next) {

            DASSERT ( ((SYMPTR)(&(p->Sym)))->reclen < 1000 );
            if (!BWrite (&p->Sym, ((SYMPTR)(&(p->Sym)))->reclen + LNGTHSZ)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    // write out the hash table

    if ( hash.symhash != 0 ) {
        for (
            NameHashBlock = VBufFirstBlock (&NameHashBuf);
            NameHashBlock != NULL;
            NameHashBlock = VBufNextBlock (NameHashBlock)
        ) {
        if (!BWrite (NameHashBlock->Address,
            NameHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    if ( hash.addrhash != 0 ) {
        for (
            AddrHashBlock = VBufFirstBlock (&AddrHashBuf);
            AddrHashBlock != NULL;
            AddrHashBlock = VBufNextBlock (AddrHashBlock)
        ) {
        if (!BWrite (AddrHashBlock->Address,
            AddrHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    PadCount = (int)(sizeof (ulong) - (Dir->cb % sizeof (ulong)));
    if ((PadCount != 4) &&
      (!BWrite (&Zero, PadCount))) {
           ErrorExit (ERR_NOSPACE, NULL, NULL);
    }

    if (verbose) {
        printf(get_err(MSG_PSYMSIZE), "\t\t", hash.cbSymbol, '\n');
    }
}





void WriteGlobalSym (OMFDirEntry *Dir, long lfoStart)
{

    OMFSymHash  hash;
    GLOBALSYM   *p;
    VBuf        NameHashBuf;
    VBlock     *NameHashBlock;
    VBuf        AddrHashBuf;
    VBlock     *AddrHashBlock;
    ushort      iHash;
    int         PadCount;
    ulong       Zero = 0;

#if 0
    DASSERT(_heapchk() == _HEAPOK);
#endif


    memset ( &hash, 0, sizeof (hash) );

    cbGlobalSym += AlignTable ( HPDBym, sizeof ( long ) );

    hash.cbSymbol = cbGlobalSym;
    BuildHash (
        &HASHFUNC,
        &hash.symhash,
        &hash.cbHSym,
        &NameHashBuf,
        cGlobalSym,
        HPDBym
    );

#if 0
    DASSERT(_heapchk() == _HEAPOK);
#endif

    BuildSort (
        &hash.addrhash,
        &hash.cbHAddr,
        &AddrHashBuf,
        cGlobalSym,
        HPDBym
    );

#if 0
    DASSERT(_heapchk() == _HEAPOK);
#endif

    Dir->SubSection = sstGlobalSym;
    Dir->iMod = 0xffff;
    Dir->lfo = lfoStart;
    Dir->cb = sizeof (OMFSymHash) + hash.cbSymbol + hash.cbHSym + hash.cbHAddr;

    // write out global symbols

    if (!BWrite ((char *)&hash, sizeof (hash))) {
        ErrorExit (ERR_NOSPACE, NULL, NULL);
    }
    for (iHash = 0; iHash < HASH_SYM; iHash++) {
        if (iHash == 0x205) {
            int i = 0;
        }
        for (p = HPDBym[iHash]; p != NULL; p = p->Next) {
            if (!BWrite (&p->Sym, ((SYMPTR)(&(p->Sym)))->reclen + LNGTHSZ)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    // write out the hash table

    if ( hash.symhash != 0 ) {
        for (
            NameHashBlock = VBufFirstBlock (&NameHashBuf);
            NameHashBlock != NULL;
            NameHashBlock = VBufNextBlock (NameHashBlock)
        ) {
        if (!BWrite (NameHashBlock->Address,
            NameHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    if ( hash.addrhash != 0 ) {
        for (
            AddrHashBlock = VBufFirstBlock (&AddrHashBuf);
            AddrHashBlock != NULL;
            AddrHashBlock = VBufNextBlock (AddrHashBlock)
        ) {
        if (!BWrite (AddrHashBlock->Address,
            AddrHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    PadCount = (int)(sizeof (ulong) - (Dir->cb % sizeof (ulong)));
    if ((PadCount != 4) &&
      (!BWrite (&Zero, PadCount))) {
           ErrorExit (ERR_NOSPACE, NULL, NULL);
    }

    if (verbose) {
        printf(get_err(MSG_SYMSIZE), "\t\t", InitialSymInfoSize, '\n', "\t\t", FinalSymInfoSize, '\n', "\t\t", hash.cbSymbol, '\n');
    }
}





/**     LinkScope - link lexicals scope
 *
 *      LinkScope (pSym, cbSym)
 *
 *      Entry   pSym = pointer to symbol table
 *              cbSym = count of bytes in symbol table
 *
 *      Exit    lexical scopes in symbol table linked
 *
 *      Returns TRUE if scopes linked
 *              FALSE if error linking scopes
 */


bool_t LinkScope (uchar *pStart, ulong cbSym)
{
    SYMPTR      pEnd;
    SYMPTR      pPrev;
    SYMPTR      pSym;
    SYMPTR      pInit;
    SEARCHPTR   pSearch;
    ulong       offParent = 0;
    long        Level = 0;

    // fill in the parent and end fields

    pSym = (SYMPTR)(pStart + sizeof (long));
    pSearch = (SEARCHPTR)pSym;
    pEnd = (SYMPTR)(pStart + cbSym);

    while( pSym < pEnd ) {
        switch( ((SYMPTR)pSym)->rectyp) {
            case S_LPROC16:
            case S_GPROC16:
            case S_THUNK16:
            case S_BLOCK16:
            case S_WITH16:
            case S_LPROC32:
            case S_GPROC32:
            case S_THUNK32:
            case S_BLOCK32:
            case S_WITH32:
            case S_LPROCMIPS:
            case S_GPROCMIPS:
                // note that this works because all of these symbols
                // have a common format for the first fields.  The
                // address variants follow the link fields.

                // put in the parent
                ((BLOCKPTR)pSym)->pParent = offParent;
                offParent = (uchar *)pSym - pStart;
                Level++;
                break;

            case S_END:
                if (Level > 0) {
                    // fill in the end record to the parent
                    ((BLOCKPTR)(pStart + offParent))->pEnd =
                      (ulong)((uchar *)pSym - pStart);

                    // reclaim his parent as the parent

                    offParent = ((BLOCKPTR)(pStart + offParent))->pParent;
                    Level--;
                } else {
                    puts("Unbalanced S_END record encountered");
                }
                break;
        }
        pSym = (SYMPTR)((uchar *)pSym + pSym->reclen + LNGTHSZ);
    }

    // Go to the end of the Search part of the symbols table

    pInit = (SYMPTR)pSearch;
    while (pInit < pEnd && pInit->rectyp == S_SSEARCH) {
        pInit = (SYMPTR)((uchar *)pInit + pInit->reclen + LNGTHSZ);
    }

    // Fill in the next fields

    while (((SYMPTR)pSearch < pEnd) && (pSearch->rectyp == S_SSEARCH)) {
        pPrev = (SYMPTR) pSearch;
        pSym = pInit;
        offParent = 0;

        while (pSym < pEnd) {
            switch ( ((SYMPTR)pSym)->rectyp ) {
                case S_LPROC16:
                case S_GPROC16:
                case S_THUNK16:
                case S_LPROC32:
                case S_GPROC32:
                case S_THUNK32:

                    if (((PROCPTR)pSym)->pEnd == 0) {
                        // bad lexical scope data
                        return (FALSE);
                    }
                    if (((SYMPTR)pPrev)->rectyp == S_SSEARCH) {
                        ((SEARCHPTR)pPrev)->startsym =
                          (ulong)((uchar *)pSym - pStart);
                    }
                    else {
                        ((PROCPTR)pPrev)->pNext = (ulong)((uchar *)pSym - pStart);
                    }
                    pPrev = pSym;
                    pSym = (SYMPTR)((uchar *)pSym + pSym->reclen + LNGTHSZ);
                    break;

                default:
                    pSym = (SYMPTR)((uchar *)pSym + pSym->reclen + LNGTHSZ);
            }
        }
        pSearch = (SEARCHPTR)((uchar *)pSearch + pSearch->reclen + LNGTHSZ);
    }
    return (TRUE);
}




bool_t SegmentPresent (ushort defaultseg)
{
    ushort i;

    for (i = 0; i < cSeg; i ++) {
        if (segnum[i] == defaultseg) {
            return (TRUE);
        }
    }
    return (FALSE);
}

ushort AddSearchSym (uchar *pSym, ushort seg)
{
    uchar      *pPad;
    int         iPad;
    int         len;

    len = ALIGN4 (sizeof (SEARCHSYM));
    ((SEARCHPTR)pSym)->reclen = len - LNGTHSZ;
    iPad = PAD4 (sizeof (SEARCHSYM));

    // Fill in all the fields with the data passed.
    ((SEARCHPTR)pSym)->rectyp = S_SSEARCH;
    ((SEARCHPTR)pSym)->seg = seg;
    ((SEARCHPTR)pSym)->startsym = 0;
    pPad = pSym + len - LNGTHSZ;
    PADLOOP (iPad, pPad);
    return (len);
}







/**
 *
 *  AddDerivationListsToTypeSegment
 *
 *  Finally put the lists into the type segment
 *
 */


LOCAL void AddDerivationListsToTypeSegment (void)
{
    ushort          i,j,l,m;
    plfClass        BaseClassString;
    uchar          *ScratchString;
    uchar          *NewString;
    HSSTRING       *k;
    plfDerived      plfDer;
    PDCLASS         DerivStructure;
    uchar         **pBuf;

    if (NoMoTypes) {
        return;
    }

    for (i = 0; i < NextInDerivation; i ++) {
        DerivStructure = &DLists[i];
        j = DerivStructure->Count;
        ScratchString = GetScratchString (2 * j +
          offsetof (lfDerived, drvdcls) + LNGTHSZ);
        NewString = ScratchString;

        // Set Length of type record and make pointer to derived leaf and
        // fill in the derived record

        ((TYPPTR)ScratchString)->len = 2 * j + offsetof (lfDerived, drvdcls);
        plfDer = (plfDerived)&(((TYPPTR)ScratchString)->leaf);
        plfDer->leaf = LF_DERIVED;
        plfDer->count = j;
        m = 0;
        for (; j > 0; j --) {
            plfDer->drvdcls[m++] = DerivStructure->List[j - 1];
        }
        free (DerivStructure->List);

        // Check for a matching LF_DERIVED record

        l = StringHash (NewString);
        for (k = HPDBtring[l]; k != NULL; k = k->Next) {
            if (memcmp ((uchar *)k->TypeString, NewString,
              k->TypeString->len + LNGTHSZ) == 0) {
                break;
            }
        }

        // Add the new record to the global index table if there wasn't a match
        // Then store the new record index in the base class.

        pBuf = pGType[(DerivStructure->BClass - CV_FIRST_NONPRIM) / GTYPE_INC];
        BaseClassString  = (plfClass)(pBuf[(DerivStructure->BClass -
          CV_FIRST_NONPRIM) % GTYPE_INC] + LNGTHSZ);
        if (k == NULL) {
            k = (HSSTRING *) Alloc (sizeof (HSSTRING));
            k->CompactedIndex = NewIndex;
            k->TypeString = (TYPPTR) VBufCpy (&TypeBuf, NewString, ((TYPPTR)NewString)->len + LNGTHSZ);
            k->Next = HPDBtring[l];
            HPDBtring[l] = k;
            HPDBtringCnt[l]++;
            if ((NewIndex - CV_FIRST_NONPRIM) == LastGlobalTableIndex) {
                LastGlobalTableIndex += GTYPE_INC;
                pBuf = Alloc (GTYPE_INC * sizeof (uchar *));
                pGType[(NewIndex - CV_FIRST_NONPRIM) / GTYPE_INC] = pBuf;
            }
            pBuf = pGType[(NewIndex - CV_FIRST_NONPRIM) / GTYPE_INC];
            pBuf[(NewIndex - CV_FIRST_NONPRIM) % GTYPE_INC] = (uchar *)(k->TypeString);
            BaseClassString->derived = NewIndex++;
            if (NewIndex == 0) {
                Warn (WARN_65KTYPES, FormatMod (pCurMod), NULL);
                NoMoTypes = TRUE;
                return;
            }
        }
        else {
            BaseClassString->derived = k->CompactedIndex;
        }
    }
}




/**
 *
 *  StringHash
 *
 *  Hash a typestring as a string by adding in all the bytes except
 *  the linkage.  If the record type is LF_STRUCTURE, LF_CLASS, LF_UNION or
 *  LF_ENUM, only the name is used for the hash.
 */

COUNTER (cnt_StringHash);

#if DBG // {

LOCAL ushort SHHits[HASH_STRING] = {HASH_STRING * 0};

void DumpStringHashHits() {
    ushort i;

    if (DbArray[7]) {
        printf("StringHash Hits\n");
        for (i=0; i < HASH_STRING; i++) {
            if (SHHits[i]) {
                printf("%u\t%u\n", i, SHHits[i]);
            }
        }
    }
}

#endif // }

static unsigned char const rgbTl[256] = {
    0,  1,  3,  2,  6,  7,  5,  4,
    13, 12, 14, 15, 11, 10, 8,  9,
    26, 27, 25, 24, 28, 29, 31, 30,
    23, 22, 20, 21, 17, 16, 18, 19,
    52, 53, 55, 54, 50, 51, 49, 48,
    57, 56, 58, 59, 63, 62, 60, 61,
    46, 47, 45, 44, 40, 41, 43, 42,
    35, 34, 32, 33, 37, 36, 38, 39,
    105,    104,    106,    107,    111,    110,    108,    109,
    100,    101,    103,    102,    98, 99, 97, 96,
    115,    114,    112,    113,    117,    116,    118,    119,
    126,    127,    125,    124,    120,    121,    123,    122,
    93, 92, 94, 95, 91, 90, 88, 89,
    80, 81, 83, 82, 86, 87, 85, 84,
    71, 70, 68, 69, 65, 64, 66, 67,
    74, 75, 73, 72, 76, 77, 79, 78,
    210,    211,    209,    208,    212,    213,    215,    214,
    223,    222,    220,    221,    217,    216,    218,    219,
    200,    201,    203,    202,    206,    207,    205,    204,
    197,    196,    198,    199,    195,    194,    192,    193,
    230,    231,    229,    228,    224,    225,    227,    226,
    235,    234,    232,    233,    237,    236,    238,    239,
    252,    253,    255,    254,    250,    251,    249,    248,
    241,    240,    242,    243,    247,    246,    244,    245,
    187,    186,    184,    185,    189,    188,    190,    191,
    182,    183,    181,    180,    176,    177,    179,    178,
    161,    160,    162,    163,    167,    166,    164,    165,
    172,    173,    175,    174,    170,    171,    169,    168,
    143,    142,    140,    141,    137,    136,    138,    139,
    130,    131,    129,    128,    132,    133,    135,    134,
    149,    148,    150,    151,    147,    146,    144,    145,
    152,    153,    155,    154,    158,    159,    157,    156 };

static unsigned char const rgbTh[256] = {
    0,  165,    74, 239,    148,    49, 222,    123,
    40, 141,    98, 199,    188,    25, 246,    83,
    80, 245,    26, 191,    196,    97, 142,    43,
    120,    221,    50, 151,    236,    73, 166,    3,
    161,    4,  235,    78, 53, 144,    127,    218,
    137,    44, 195,    102,    29, 184,    87, 242,
    241,    84, 187,    30, 101,    192,    47, 138,
    217,    124,    147,    54, 77, 232,    7,  162,
    66, 231,    8,  173,    214,    115,    156,    57,
    106,    207,    32, 133,    254,    91, 180,    17,
    18, 183,    88, 253,    134,    35, 204,    105,
    58, 159,    112,    213,    174,    11, 228,    65,
    227,    70, 169,    12, 119,    210,    61, 152,
    203,    110,    129,    36, 95, 250,    21, 176,
    179,    22, 249,    92, 39, 130,    109,    200,
    155,    62, 209,    116,    15, 170,    69, 224,
    133,    32, 207,    106,    17, 180,    91, 254,
    173,    8,  231,    66, 57, 156,    115,    214,
    213,    112,    159,    58, 65, 228,    11, 174,
    253,    88, 183,    18, 105,    204,    35, 134,
    36, 129,    110,    203,    176,    21, 250,    95,
    12, 169,    70, 227,    152,    61, 210,    119,
    116,    209,    62, 155,    224,    69, 170,    15,
    92, 249,    22, 179,    200,    109,    130,    39,
    199,    98, 141,    40, 83, 246,    25, 188,
    239,    74, 165,    0,  123,    222,    49, 148,
    151,    50, 221,    120,    3,  166,    73, 236,
    191,    26, 245,    80, 43, 142,    97, 196,
    102,    195,    44, 137,    242,    87, 184,    29,
    78, 235,    4,  161,    218,    127,    144,    53,
    54, 147,    124,    217,    162,    7,  232,    77,
    30, 187,    84, 241,    138,    47, 192,    101};

static unsigned char const rgbTea[256] = {
    0,  151,    46, 185,    92, 203,    114,    229,
    184,    47, 150,    1,  228,    115,    202,    93,
    112,    231,    94, 201,    44, 187,    2,  149,
    200,    95, 230,    113,    148,    3,  186,    45,
    224,    119,    206,    89, 188,    43, 146,    5,
    88, 207,    118,    225,    4,  147,    42, 189,
    144,    7,  190,    41, 204,    91, 226,    117,
    40, 191,    6,  145,    116,    227,    90, 205,
    192,    87, 238,    121,    156,    11, 178,    37,
    120,    239,    86, 193,    36, 179,    10, 157,
    176,    39, 158,    9,  236,    123,    194,    85,
    8,  159,    38, 177,    84, 195,    122,    237,
    32, 183,    14, 153,    124,    235,    82, 197,
    152,    15, 182,    33, 196,    83, 234,    125,
    80, 199,    126,    233,    12, 155,    34, 181,
    232,    127,    198,    81, 180,    35, 154,    13,
    128,    23, 174,    57, 220,    75, 242,    101,
    56, 175,    22, 129,    100,    243,    74, 221,
    240,    103,    222,    73, 172,    59, 130,    21,
    72, 223,    102,    241,    20, 131,    58, 173,
    96, 247,    78, 217,    60, 171,    18, 133,
    216,    79, 246,    97, 132,    19, 170,    61,
    16, 135,    62, 169,    76, 219,    98, 245,
    168,    63, 134,    17, 244,    99, 218,    77,
    64, 215,    110,    249,    28, 139,    50, 165,
    248,    111,    214,    65, 164,    51, 138,    29,
    48, 167,    30, 137,    108,    251,    66, 213,
    136,    31, 166,    49, 212,    67, 250,    109,
    160,    55, 142,    25, 252,    107,    210,    69,
    24, 143,    54, 161,    68, 211,    106,    253,
    208,    71, 254,    105,    140,    27, 162,    53,
    104,    255,    70, 209,    52, 163,    26, 141};


static uchar bHigh;
static uchar bLow;
static uchar bRes;



INLINE void HashFun(uchar *pin) {
    bRes = rgbTl[bLow] ^ rgbTh[bHigh];
    bLow = bHigh;
    bHigh = rgbTea[(uchar)(*pin ^ bRes)];
}

LOCAL ushort _fastcall StringHash (uchar *TypeString)
{
    ushort      j, k;
    uchar      *puc;
    uchar      *pucEnd;
    TYPPTR      pType = (TYPPTR)TypeString;

    COUNT (cnt_StringHash);
    j = 0;
    pucEnd = TypeString + pType->len + sizeof (pType->len);

    switch (pType->leaf) {
        case LF_STRUCTURE:
        case LF_CLASS:
            puc = (uchar *)&(((plfClass)&pType->leaf)->data);
            puc += C7SizeOfNumeric (puc);
            break;

        case LF_UNION:
            puc = (uchar *)&(((plfUnion)&pType->leaf)->data);
            puc += C7SizeOfNumeric (puc);
            break;

        case LF_ENUM:
            puc = (uchar *)&(((plfEnum)&pType->leaf)->Name);
            break;

        default:
            puc = TypeString;
            break;
    }

    k = pucEnd - puc;
    bHigh = 0;
    bLow = 0;

    DASSERT(k <= 65535);

    // what we are doing here is a loop unrolling of the CRC16 tea-leaf
    // algorithm, i decided to go this way because so many of the type records
    // are nearly identical
    // sps - 9/3/92

    while (k)
        {
        ushort kMod8;

        kMod8 = ((k + 7) % 8);
        switch (kMod8)
            {
            case  7: HashFun (puc + 7);
            case  6: HashFun (puc + 6);
            case  5: HashFun (puc + 5);
            case  4: HashFun (puc + 4);
            case  3: HashFun (puc + 3);
            case  2: HashFun (puc + 2);
            case  1: HashFun (puc + 1);
            case  0: HashFun (puc + 0);
            }

        puc += kMod8 + 1;
        k    = pucEnd - puc;
        }

    j = (ushort)((bHigh << 8) | bLow) % HASH_STRING;

#if DBG // {
    SHHits[j]++;
#endif // }

    return (j);
}


/**
 *
 *  AddToDerivationList
 *
 *  Adds a derived class to the derivation list of a base class
 *
 */


LOCAL void AddToDerivationList (CV_typ_t DerivedClass, CV_typ_t BaseClass)
{
    plfClass    BClassStr;
    ushort      i;
    uchar     **pBuf;

    pBuf = pGType[(BaseClass - CV_FIRST_NONPRIM) / GTYPE_INC];
    BClassStr = (plfClass)(pBuf[(BaseClass - CV_FIRST_NONPRIM) % GTYPE_INC] + LNGTHSZ);
    DASSERT ((BClassStr->leaf == LF_CLASS) ||
      (BClassStr->leaf == LF_STRUCTURE));

    for (i = 0; i < NextInDerivation; i++) {
        if (DLists[i].BClass == BaseClass) {
            break;
        }
    }
    if (i == NextInDerivation) {
        // This is the first derivation for this class

        if (NextInDerivation == MaxDerivation) {
            // It won't fit in our current list, so lengthen the list.

            MaxDerivation += DCLASS_INC;
            DLists = (PDCLASS)NoErrorRealloc (DLists, MaxDerivation * sizeof (DCLASS));
        }
        DLists[i].Count = 0;
        DLists[i].Max = DLIST_INC;
        DLists[i].BClass = BaseClass;
        DLists[i].List = (CV_typ_t *)Alloc (DLIST_INC * sizeof (CV_typ_t));
        NextInDerivation++;
    }

    // Add the derived class

    if (DLists[i].Count == DLists[i].Max) {
        DLists[i].Max += DLIST_INC;
        DLists[i].List = (CV_typ_t *)NoErrorRealloc (
          DLists[i].List, DLists[i].Max * sizeof (CV_typ_t));
    }
    DLists[i].List[DLists[i].Count++] = DerivedClass;
}




/**
 *
 *  TypeHash
 *
 *  Map a type string to an integer without including the lower level
 *  type indices
 *  Uses C7 format type strings
 *
 */

COUNTER (cnt_TypeHash);

LOCAL int _fastcall TypeHash (TENTRY *OldEntry)
{
    int        i;
    ushort     j;
    int        length;
    int        index;
    uchar     *TypeString;
    ushort     hashval = 0;
    uchar     *puc;
    TYPPTR     pType = (TYPPTR)OldEntry->TypeString;

    if (OldEntry->Hash)
        return OldEntry->Hash;

    OldEntry->Hash = HASH_TYPE - 1; // set tmp hash so we don't recurse

    COUNT (cnt_TypeHash);
    hashval =  (pType->leaf);

    switch (pType->leaf) {
        case LF_STRUCTURE:
        case LF_CLASS:
            puc = (uchar *)&(((plfClass)&pType->leaf)->data);
            puc += C7SizeOfNumeric (puc);
            break;

        case LF_UNION:
            puc = (uchar *)&(((plfUnion)&pType->leaf)->data);
            puc += C7SizeOfNumeric (puc);
            break;

        case LF_ENUM:
            puc = (uchar *)&(((plfEnum)&pType->leaf)->Name);
            break;

        case LF_MEMBER:
            puc = (uchar *)&(((plfMember)&pType->leaf)->offset);
            puc += C7SizeOfNumeric (puc);
            break;

        case LF_STMEMBER:
            puc = (uchar *)&(((plfSTMember)&pType->leaf)->Name);
            break;

        default:
            TypeString = OldEntry->TypeString;
            length = C7LENGTH (TypeString) + LNGTHSZ;
            j = 0;
            i = 0;
            while (i < length) {
                if (j == OldEntry->Count) {
                    index = length;
                }
                else {
                    if (OldEntry->flags.LargeList) {
                       index = OldEntry->IndexUnion.IndexString[j];
                   }
                    else {
                       index = OldEntry->IndexUnion.Index[j];
                    }
                    j ++;
                }
                while (i < index) {
                    hashval = (hashval << 2) ^  TypeString[i++] ^ (hashval >> 14);
                }
                i += sizeof (CV_typ_t);
            }

            // check no more than 3 nested subtypes...

            i = OldEntry->Count;

            if (i > 3) {
                i = 3;
            }

            while (--i >= 0) {
                if (!IndexIsGlobal(OldEntry, (ushort) i)) {
                    hashval ^= SubHash(OldEntry, (ushort) i);
                }
            }

            OldEntry->Hash = (hashval % HASH_TYPE);
            return OldEntry->Hash;
    }

    length = *puc++;
    while (--length > 0) {
        hashval = (hashval << 2) ^ (*puc++) ^ (hashval >> 14);
    }

    OldEntry->Hash = (hashval % HASH_TYPE);

    return OldEntry->Hash;
}

LOCAL ushort _fastcall SubHash (TENTRY *OldEntry, ushort iitem)
{
    TYPPTR     pType = (TYPPTR)OldEntry->TypeString;
    CV_typ_t   forward;
    CV_typ_t   next;
    TENTRY *   TmpLocalEntry;
    ushort     index;

    if (OldEntry->flags.LargeList) {
        index = OldEntry->IndexUnion.IndexString[iitem];
    }
    else {
        index = OldEntry->IndexUnion.Index[iitem];
    }

    next = *(CV_typ_t *)((uchar *)pType + index);

    if (next < usCurFirstNonPrim)
        return next;

    next -= usCurFirstNonPrim;

    TmpLocalEntry = GetTypeEntry (next, &forward);

    if (TmpLocalEntry->flags.IsInserted)
        return (TmpLocalEntry->Hash);
    else
        return (TypeHash(TmpLocalEntry));
}


INLINE void CleanUpModuleIndexTable (void)
{
    free(ModNdxTab);
}

#if 1 // { GTW: inline guard condition to FreeAllocStrings.

void FreeAllocStrings_ (TENTRY *OldEntry)

#else

void FreeAllocStrings (TENTRY *OldEntry)

#endif // }

{
    if (OldEntry->flags.IsMalloced) {
        free (OldEntry->TypeString);
        OldEntry->flags.IsMalloced = FALSE;
    }
    else if (OldEntry->flags.IsPool2) {
        Pool2Free (OldEntry->TypeString);
        OldEntry->flags.IsPool2 = FALSE;
    }
    else if (OldEntry->flags.IsPool) {
        PoolFree (OldEntry->TypeString);
        OldEntry->flags.IsPool = FALSE;
    }
}


/**     BuildHash
 *
 *      Build a global symbol hash table using the hash function
 *      passed in lpfnHash
 *
 *      The format of this table is as follows:
 *
 *      Position    Size    Description
 *
 *      0           2       # of hash entries
 *      2           2       filler to preserve nat. alignment
 *      4           4*n     Hash table, each entry is the offset from
 *                              the start of this table to the
 *                              chain associated with its index.
 *      4+4*n       2*n     Count of entries in each hash bucket
 *                              chain associated with its index.
 *      4 + 6*n     4*m     Chain table, each entry is a length prefixed
 *                              list of offsets to the symbols associated with
 *                              this index.
 *
 *      NOTE: All offsets are ulongs
 *
 */

LOCAL void BuildHash (
    LPFNHASH lpfnHash,
    ushort  *pusHashId,
    ulong   *pcbHash,
    VBuf    *HashBuf,
    ulong   cSym,
    GLOBALSYM **HT

) {
    ulong       ulSymbolAddr = 0;
    ushort      HashSize;
    PECT       *rgpect;
    POVFL      rgpecet [ cpecetMax ] = { NULL };
    ushort      cpecet = 0;
    ushort      cect = 0;
    ushort      iect = 0;
    ulong       cbAlloc;
    ushort      index;
    PECT       *ppect;
    PECT        pect;
    ushort      ipecet;
    ushort      iHash;
    ulong       ulChain;
    POVFL       pOvfl;
    POVFL      *ppOvfl;
    ushort      culSymbol;
    ushort      ipect;
    GLOBALSYM  *p;

    if (cSym == 0) {
        return;
    }

    // compute size of hash table and allow for initial zero length.
    // the hashsize is always rounded to an even number so that the
    // count array is aligned.  The intent is to minimize the average
    // length of the bucket to about cBucketSize entries per chain.  We
    // are also assuming that the number of entries per bucket is less
    // than 65k.

    HashSize = (ushort) min (cSym / cBucketSize, 0xFFFF);
    HashSize = max (HashSize, MINHASH);
    HashSize = (HashSize + 1) & ~1;

    // Set up the chain tables for each bucket.  rgpect is an array that
    // contains pointers to portions of the array of hash entry structures.
    // The hash entry structures contain the count of symbols in this bucket,
    // the offsets of the first cBucketSize symbols in the bucket and a pointer
    // to the overflow chain.

    cect = HashSize / cectMax + 1;
    rgpect = Alloc (cect * sizeof (PECT));
    for (iect = 0; iect < cect; iect++) {
        cbAlloc = (iect == cect-1) ?
          (((long)HashSize * sizeof (ECT)) % (cectMax * sizeof (ECT))) :
          cectMax * sizeof (ECT);
        DASSERT (cbAlloc <= UINT_MAX);
        *(rgpect + iect) = CAlloc ((size_t)cbAlloc);
    }

    // loop through all of the symbols generating hash information
    // for each symbol

    for (iHash = 0; iHash < HASH_SYM; iHash++) {
        for (p = HT[iHash]; p != NULL; p = p->Next) {
            // compute hash index, pointer to pointer to chain and
            // pointer to chain

            if ( ((SYMPTR) (&p->Sym))->rectyp == S_ALIGN ) {
                ulSymbolAddr  += ((SYMPTR)(&p->Sym))->reclen + LNGTHSZ;
                continue;
            }

            index = (ushort) (p->sumName % HashSize);

            ppect = rgpect + (index / cectMax);
            pect  = *ppect + (index % cectMax);

            if (pect->culSymbol < culEct) {
                // the bucket has not overflowed

                pect->rgsoh[pect->culSymbol].uoff   = ulSymbolAddr;
                pect->rgsoh[pect->culSymbol].ulHash = p->sumName;
            }
            else {
                // the bucket has overflowed.  Walk the chain of
                // overflow buffers to the final bucket and store
                // the symbol offset

                ppOvfl = &pect->pOvflNext;
                culSymbol = (pect->culSymbol - culEct) % OVFL_C;

                while (*ppOvfl != NULL &&
                  (culSymbol == 0 || (*ppOvfl)->pOvflNext != NULL)) {
                    ppOvfl = &(*ppOvfl)->pOvflNext;
                }
                if (culSymbol == 0) {
                    ipecet = cpecet / cecetMax;
                    if (rgpecet [ ipecet ] == NULL) {
                        rgpecet [ ipecet ] = CAlloc (cbMaxAlloc);
                    }
                    *ppOvfl = rgpecet [ ipecet ] + ( cpecet % cecetMax );
                    cpecet += 1;
                }
                (*ppOvfl)->rgsoh[ culSymbol ].uoff   = ulSymbolAddr;
                (*ppOvfl)->rgsoh[ culSymbol ].ulHash = p->sumName;

            }
            pect->culSymbol += 1;
            ulSymbolAddr  += ((SYMPTR)(&p->Sym))->reclen + LNGTHSZ;
        }
    }

    // Now put all of this information into the VBuf

    VBufInit (HashBuf, NameHashBlockSize);

    // Write out the table size preserving natural alignment

    VBufCpy (HashBuf, (uchar *) &HashSize, sizeof (HashSize));
    VBufSet (HashBuf, 0, sizeof (ushort));

    // Write out the actual hash table

    ulChain = 0;

    for (iHash = 0; iHash < HashSize; iHash++) {
        ppect = rgpect + (iHash / cectMax);
        pect  = *ppect + (iHash % cectMax);

        VBufCpy (HashBuf, (uchar *)&ulChain, sizeof (ulChain));
        ulChain += pect->culSymbol * 2 * sizeof (ulong);
    }

    // Write out the bucket counts

    for (iHash = 0; iHash < HashSize; iHash++) {
        ppect = rgpect + (iHash / cectMax);
        pect  = *ppect + (iHash % cectMax);
        VBufCpy (HashBuf, (uchar *)&pect->culSymbol, sizeof (culSymbol));
        VBufSet (HashBuf, 0, sizeof ( ushort ) );
    }

    // Write out the chains

    for (iHash = 0; iHash < HashSize; iHash++) {
        ppect     = rgpect + (iHash / cectMax);
        pect      = *ppect + (iHash % cectMax);
        pOvfl     = pect->pOvflNext;
        culSymbol = pect->culSymbol;

        VBufCpy (
            HashBuf,
            (uchar *)pect->rgsoh,
            min (culEct, culSymbol) * sizeof (SOH)
        );
        culSymbol -= min (culEct, culSymbol);
        while (culSymbol > 0) {
            VBufCpy (
                HashBuf,
                (uchar *)pOvfl->rgsoh,
                min (OVFL_C, culSymbol) * sizeof (SOH)
            );
            culSymbol -= min (OVFL_C, culSymbol);
            pOvfl = pOvfl->pOvflNext;
        }
    }
    *pusHashId = 10;
    *pcbHash =
        sizeof (HashSize) +
        sizeof (ushort) +
        sizeof (ulong) * HashSize * 2 +
        sizeof (SOH) * cSym;

    // Now free all of our internal structures

    for (ipect = 0; ipect < cect; ipect++) {
        free (*(rgpect + ipect));
    }
    free (rgpect);
    for (ipecet = 0; ipecet < cpecetMax; ipecet++) {
        free (rgpecet [ipecet]);
    }
}


typedef struct _EST {
    GLOBALSYM *pglb;
    ulong      ulsym;
} EST; // Entry in Sort Table
typedef EST *PEST;

typedef struct _SGN {
    int  csym;
    int  isym;
    PEST rgest;
} SGN; // SeGment Node
typedef SGN *PSGN;

LOCAL int __cdecl GlbAddrCmp ( const void *pv1, const void *pv2 ) {
    const EST *pest1 = pv1;
    const EST *pest2 = pv2;

    return (int) ( pest1->pglb->uoff - pest2->pglb->uoff );
}


LOCAL void BuildSort (
    ushort  *pusHashId,
    ulong   *pcbHash,
    VBuf    *SortBuf,
    ulong   cSym,
    GLOBALSYM **HT

) {
    ushort cseg = 0;
    int   iseg;
    int   isym;
    int   iHash;
    ulong ulsrt;
    PSGN  rgsgn = NULL;
    ulong ulsym = 0;
    GLOBALSYM *p = NULL;
    int   csymBogus = 0;

    if ( cSym == 0 ) {
        return;
    }

    // Find the number of segments used by the publics

    for ( iHash = 0; iHash < HASH_SYM; iHash++ ) {
        for ( p = HT[iHash]; p != NULL; p = p->Next ) {
            if (
                ((SYMPTR) (&p->Sym))->rectyp != S_ALIGN &&
                p->seg > (ushort) cseg
            ) {
                cseg = p->seg;
            }
        }
    }

    // Allocate an array containing global information for each segment

    rgsgn = CAlloc ( sizeof ( SGN ) * cseg );

    for ( iHash = 0; iHash < HASH_SYM; iHash++ ) {
        for ( p = HT[iHash]; p != NULL; p = p->Next ) {
            if ( ((SYMPTR) (&p->Sym))->rectyp == S_ALIGN ) {
                // Do nothing -- cSym doesn't include align symbols
            }
            else if ( p->seg == 0 ) {
                csymBogus += 1;
            }
            else {
                rgsgn [ p->seg - 1 ].csym += 1;
            }
        }
    }

    // Allocate memory for the list of symbols associated w/ each segment

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {
        if ( rgsgn [ iseg ].csym ) {
            rgsgn [ iseg ].rgest = CAlloc ( rgsgn [ iseg ].csym * sizeof (EST) );
        }
    }

    // Associate a pointer to each symbol with the appropriate segment

    for ( iHash = 0; iHash < HASH_SYM; iHash++ ) {
        for ( p = HT[iHash]; p != NULL; p = p->Next ) {
            PEST pest = NULL;

            if ( ((SYMPTR) (&p->Sym))->rectyp != S_ALIGN ) {

                iseg = p->seg - 1;

                if ( iseg != -1 ) {
                    pest = &rgsgn [ iseg ].rgest [ rgsgn [ iseg ].isym++ ];
                    pest->pglb  = p;
                    pest->ulsym = ulsym;
                }
            }

            ulsym += ((SYMPTR)(&p->Sym))->reclen + LNGTHSZ;
        }
    }

    // Now sort the symbols within each segment

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {
        PSGN psgn = &rgsgn [ iseg ];

        qsort ((void *)psgn->rgest, (size_t) psgn->csym, sizeof (EST), GlbAddrCmp );
    }

    // Write the sorted table out to the vbuf

    VBufInit (SortBuf, NameHashBlockSize);

    // Write out the number of segments preserving natural alignment

    VBufCpy (SortBuf, (uchar *) &cseg, sizeof (cseg) );
    VBufSet (SortBuf, 0, sizeof (ushort));

    // Write out the position of each segment's info

    ulsrt = 0;

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {
        VBufCpy (SortBuf, (uchar *) &ulsrt, sizeof ( ulong ) );
        ulsrt += rgsgn [ iseg ].csym * ( 2 * sizeof ( ulong ) );
    }

    // Write out the count of symbols in each segment

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {

        // Note that this should really be a long rather than a short
        //  bufferd by a 0 word, but that would requires a more global
        //  change to this function, which would be too high impact
        //  for right now.

        VBufCpy (SortBuf, (uchar *) &(rgsgn [ iseg ].csym), sizeof ( ushort ) );
        VBufSet (SortBuf, 0, sizeof ( ushort ) );
    }

    // Write out the sorted list of publics associated w/ each segment

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {
        PSGN psgn = &rgsgn [ iseg ];

        for ( isym = 0; isym < psgn->csym; isym++ ) {
            VBufCpy (
                SortBuf,
                (uchar *) &(psgn->rgest [ isym ].ulsym),
                sizeof ( ulong )
            );
            VBufCpy (
                SortBuf,
                (uchar *) &(psgn->rgest [ isym ].pglb->uoff),
                sizeof ( ulong )
            );
        }
    }

    *pusHashId = 12;
    *pcbHash =
        sizeof (cseg) +
        sizeof (ushort) +
        sizeof (ulong) * cseg * 2 +
        sizeof (ulong) * ( cSym - csymBogus ) * 2;

    // Free the temporary memory

    for ( iseg = 0; iseg < (int) cseg; iseg++ ) {
        if ( rgsgn [ iseg ].rgest ) {
            free ( rgsgn [ iseg ].rgest );
        }
    }

    free ( rgsgn );

}


#define FNH_SIZE 50

typedef struct _FNH {
    unsigned char   *szFileName;
    ulong           ulFileOffset;
    struct _FNH     *pfnhOrder;
    struct _FNH     *pfnhNext;
} FNH; // File Name Hash
typedef FNH *PFNH;

PFNH rgpfnh [ FNH_SIZE ];

typedef struct _MFO {
    ushort       imod;
    ushort       cFiles;
    struct _MFO *pmfoNext;
    ulong        rgulFileOffset [ ];
} MFO; // Module File Offsets
typedef MFO *PMFO;

PFNH AddFileName ( unsigned char *pch, PFNH pfnhPrev, ulong ulFileOffset ) {
    // Assumes that filename does not already exist
    // Returns the structure created

    PFNH  pfnhT = Alloc ( sizeof ( FNH ) );

    pfnhT->szFileName = pch;
    pfnhT->ulFileOffset = ulFileOffset;
    pfnhT->pfnhOrder = NULL;

    pfnhPrev->pfnhOrder = pfnhT;

    return pfnhT;
}



void BuildFileIndex ( void ) {
    PMOD   pmod   = NULL;
    ushort cfilerefs = 0;
    FNH    fnhHead = {0};
    PFNH   pfnhPrev = &fnhHead;
    PFNH   pfnh = NULL;
    PFNH   pfnhNext = NULL;
    ulong  ulFileOffset = 0;
    MFO    mfoHead = { 0 };
    PMFO   pmfoPrev = &mfoHead;
    PMFO   pmfo = NULL;
    PMFO   pmfoNext = NULL;
    OMFFileIndex *pfit = NULL;
    ushort imod = 0;
    ushort iulNames = 0;
    ulong *pulNames = NULL;
    unsigned char  *pch = NULL;

    // Set up an intermediate structure

    memset ( rgpfnh, 0, sizeof ( PFNH ) * FNH_SIZE );

    for ( pmod = ModuleList; pmod != NULL; pmod = pmod->next ) {
        OMFSourceModule *psmi = (OMFSourceModule *) pmod->SrcLnAddr;

        if ( psmi != NULL ) {
            int  isf  = 0;
            PMFO pmfoT = Alloc ( sizeof ( MFO ) + sizeof ( ulong ) * psmi->cFile );

            pmfoT->imod = pmod->ModuleIndex;
            pmfoT->pmfoNext = NULL;
            pmfoT->cFiles = psmi->cFile;
            pmfoPrev->pmfoNext = pmfoT;
            pmfoPrev = pmfoT;

            for ( isf = 0; isf < (int) psmi->cFile; isf++ ) {
                OMFSourceFile *psfi =
                    (OMFSourceFile *) ( psmi->baseSrcFile[isf] + pmod->SrcLnAddr );

                unsigned char *pch =
                    ( (char *) psfi->baseSrcLn ) +
                    psfi->cSeg * ( sizeof ( ulong ) * 3 );

                ulong ulFOT = 0;

                pfnhPrev = AddFileName ( pch, pfnhPrev, ulFileOffset );

                pmfoT->rgulFileOffset [ isf ] = ulFileOffset;

                ulFileOffset += *pch + 1;

                cfilerefs += 1;
            }
        }
    }

    // Re-format as byte stream to written to disk


    FileIndexSize =
        sizeof ( ushort ) * 2 +         // Initial fields
        sizeof ( ushort ) * 2 * cMod +  // Module list & cfiles
        sizeof ( ulong  ) * cfilerefs + // String offsets
        pfnhPrev->ulFileOffset +        // String table
            *( pfnhPrev->szFileName ) + 1;
    FileIndexSize = ( FileIndexSize + 3 ) & ~3L;

    pfit = Alloc ( FileIndexSize );

    FileIndex = (_vmhnd_t) pfit;

    pfit->cmodules = cMod;
    pfit->cfilerefs = cfilerefs;

    // Handle modulelist, cfiles, & ulNames arrays

    imod = 1;
    iulNames = 0;
    pulNames = (ulong *) &pfit->modulelist [ cMod * 2 ];

    for ( pmfo = mfoHead.pmfoNext; pmfo != NULL; pmfo = pmfo->pmfoNext ) {

        // Fill in modules w/o source lines

        while ( imod < pmfo->imod ) {
            pfit->modulelist [ imod - 1 ] = 0;
            pfit->modulelist [ cMod + imod - 1 ] = 0;
            imod += 1;
        }

        pfit->modulelist [ imod - 1 ] = iulNames;
        pfit->modulelist [ cMod + imod - 1 ] = pmfo->cFiles;
        iulNames += pmfo->cFiles;

        memcpy ( pulNames, pmfo->rgulFileOffset, sizeof ( ulong ) * pmfo->cFiles );

        pulNames += pmfo->cFiles;

        imod += 1;
    }

    // Fill in trailing modules w/o source lines

    while ( imod < cMod + 1 ) {
        pfit->modulelist [ imod - 1 ] = 0;
        pfit->modulelist [ cMod + imod - 1 ] = 0;
        imod += 1;
    }

    // Handle Name strings

    pch = (char *) &pfit->modulelist [ cMod * 2 + cfilerefs * 2 ];

    for ( pfnh = fnhHead.pfnhOrder; pfnh != NULL; pfnh = pfnh->pfnhOrder ) {

        memcpy ( pch, pfnh->szFileName, (size_t)*pfnh->szFileName + 1 );

        pch += *pch + 1;
    }

    // Free the temporary structures

    for ( pmfo = mfoHead.pmfoNext; pmfo != NULL; pmfo = pmfoNext ) {
        pmfoNext = pmfo->pmfoNext;

        free ( pmfo );
    }

    for ( pfnh = fnhHead.pfnhOrder; pfnh != NULL; pfnh = pfnhNext ) {
        pfnhNext = pfnh->pfnhOrder;

        free ( pfnh );
    }
}

void AddSymToTable (
    SYMPTR pSym,
    GLOBALSYM **HT,
    int imod,
    ulong ibSym,
    ushort rectyp
) {
    ushort      hashName;
    ulong       sumName;
    ushort      indName;
    GLOBALSYM  *pNew;
    uint        cbReqd;
    ushort      seg  = 0;
    ulong       uoff = 0;
    REFSYM     *prs;

    hashName = HASHFUNC (
        pSym,
        &sumName,
        &indName,
        &seg,
        &uoff,
        HASH_SYM
    );

    cbReqd = sizeof (GLOBALSYM) + sizeof (REFSYM);

    pNew = AllocForHT ( cbReqd );

    pNew->sumName = sumName;
    pNew->indName = indName;
    pNew->seg     = seg;
    pNew->uoff    = uoff;

    prs           = (REFSYM *) (pNew->Sym);
    prs->reclen   = sizeof ( REFSYM ) - LNGTHSZ;
    prs->rectyp   = rectyp;
    prs->sumName  = sumName;
    prs->imod     = imod;
    prs->ibSym    = ibSym;

    pNew->Next = HT[hashName];
    HT[hashName] = pNew;
}

void BuildStatics ( SYMPTR psymStart, ulong cbSym, int imod ) {
    ulong  ibSym = sizeof ( ulong );
    SYMPTR psym  = (SYMPTR) (( (uchar *) psymStart ) + sizeof ( ulong ));

    while ( ibSym < cbSym ) {

        switch ( psym->rectyp ) {

            case S_GPROC16:
            case S_GPROC32:
            case S_GPROCMIPS:

                AddSymToTable ( psym, HPDBym, imod, ibSym, S_PROCREF );
                ibSym = ( (PROCPTR) psym )->pEnd;
                psym  = (SYMPTR) ( ((uchar *) psymStart) + ibSym );
                cbGlobalSym += sizeof ( REFSYM );
                cGlobalSym++;
                break;

            case S_LPROC16:
            case S_LPROC32:
            case S_LPROCMIPS:

                AddSymToTable ( psym, HTLoc, imod, ibSym, S_PROCREF );
                ibSym = ( (PROCPTR) psym )->pEnd;
                psym  = (SYMPTR) ( ((uchar *) psymStart) + ibSym );
                cbStaticSym += sizeof ( REFSYM );
                cStaticSym++;
                break;

            case S_LDATA16:
            case S_GDATA16:
            case S_LDATA32:
            case S_GDATA32:
            case S_LTHREAD32:
            case S_GTHREAD32:

                AddSymToTable ( psym, HTLoc, imod, ibSym, S_DATAREF );
                cbStaticSym += sizeof ( REFSYM );
                cStaticSym++;
                break;

            default:
                break;

        }

        // For procs we are now pointing to the end symbol, all others
        //  we are pointing to the symbol we just processed

        ibSym += psym->reclen + LNGTHSZ;
        psym   = (SYMPTR) ( ((uchar *) psym) + psym->reclen + LNGTHSZ );
    }
}


void WriteStaticSym (OMFDirEntry *Dir, long lfoStart)
{

    OMFSymHash  hash;
    GLOBALSYM   *p;
    VBuf        NameHashBuf;
    VBlock     *NameHashBlock;
    VBuf        AddrHashBuf;
    VBlock     *AddrHashBlock;
    ushort      iHash;
    int         PadCount;
    ulong       Zero = 0;

    memset ( &hash, 0, sizeof (hash) );

    cbStaticSym += AlignTable ( HTLoc, 0 );

    hash.cbSymbol = cbStaticSym;
    BuildHash (
        &HASHFUNC,
        &hash.symhash,
        &hash.cbHSym,
        &NameHashBuf,
        cStaticSym,
        HTLoc
    );

    BuildSort (
        &hash.addrhash,
        &hash.cbHAddr,
        &AddrHashBuf,
        cStaticSym,
        HTLoc
    );

    Dir->SubSection = sstStaticSym;
    Dir->iMod = 0xffff;
    Dir->lfo = lfoStart;
    Dir->cb = sizeof (OMFSymHash) + hash.cbSymbol + hash.cbHSym + hash.cbHAddr;

    // write out static symbols

    if (!BWrite ((char *)&hash, sizeof (hash))) {
        ErrorExit (ERR_NOSPACE, NULL, NULL);
    }
    for (iHash = 0; iHash < HASH_SYM; iHash++) {
        if (iHash == 0x205) {
            int i = 0;
        }
        for (p = HTLoc[iHash]; p != NULL; p = p->Next) {
            if (!BWrite (&p->Sym, ((SYMPTR)(&(p->Sym)))->reclen + LNGTHSZ)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    // write out the hash table

    if ( hash.symhash != 0 ) {
        for (
            NameHashBlock = VBufFirstBlock (&NameHashBuf);
            NameHashBlock != NULL;
            NameHashBlock = VBufNextBlock (NameHashBlock)
        ) {
        if (!BWrite (NameHashBlock->Address,
            NameHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    if ( hash.addrhash != 0 ) {
        for (
            AddrHashBlock = VBufFirstBlock (&AddrHashBuf);
            AddrHashBlock != NULL;
            AddrHashBlock = VBufNextBlock (AddrHashBlock)
        ) {
        if (!BWrite (AddrHashBlock->Address,
            AddrHashBlock->Size)) {
                ErrorExit (ERR_NOSPACE, NULL, NULL);
            }
        }
    }

    PadCount = (int)(sizeof (ulong) - (Dir->cb % sizeof (ulong)));
    if ((PadCount != 4) &&
      (!BWrite (&Zero, PadCount))) {
           ErrorExit (ERR_NOSPACE, NULL, NULL);
    }
}

ulong AlignTable ( GLOBALSYM **HT, int cbMinFill ) {
    int    ipgs   = 0;
    ushort offSym = 0;
    ushort cbMinAlignSym = cbMinFill + sizeof ( ALIGNSYM );
    ulong  offExtra = 0;

    for ( ipgs = 0; ipgs < HASH_SYM ; ipgs++ ) {
        GLOBALSYM *pgs     = HT [ ipgs ];
        GLOBALSYM *pgsPrev = NULL;

        while ( pgs != NULL ) {

            if (
                offSym +
                    ( (SYMPTR) pgs->Sym )->reclen +
                    LNGTHSZ +
                    cbMinAlignSym >
                (ushort) cbAlign
            ) {
                // Insert an alignment symbol

                uint       cbSym = cbAlign - offSym;
                GLOBALSYM *pgsT  = AllocForHT ( (sizeof ( GLOBALSYM ) + cbSym) );
                SYMPTR     psym  = (SYMPTR) pgsT->Sym;

                pgsT->sumName = 0xFFFF;
                pgsT->indName = 0xFFFF;
                pgsT->seg     = 0;
                pgsT->uoff    = 0;

                memset ( psym, 0, cbSym );
                psym->reclen = cbSym - LNGTHSZ;
                psym->rectyp = S_ALIGN;

                pgsT->Next = pgs;
                if ( pgsPrev ) {
                    pgsPrev->Next = pgsT;
                }
                else {
                    HT [ ipgs ] = pgsT;
                }

                offExtra += cbSym;
                offSym = 0;
            }

            offSym +=
                ( (SYMPTR) pgs->Sym )->reclen + LNGTHSZ;


            pgsPrev = pgs;
            pgs = pgs->Next;
        }

        if (
            ipgs == HASH_SYM - 1 &&
            cbMinFill >= sizeof ( ulong )
        ) {
            // Insert an end symbol
            uint       cbSym = 2 * sizeof ( ulong );
            GLOBALSYM *pgsT  = AllocForHT ( (sizeof ( GLOBALSYM ) + cbSym) );
            SYMPTR     psym  = (SYMPTR) pgsT->Sym;

            pgsT->sumName = 0xFFFF;
            pgsT->indName = 0xFFFF;
            pgsT->seg     = 0;
            pgsT->uoff    = 0;

            memset ( psym, 0xFF, cbSym );
            psym->reclen = cbSym - LNGTHSZ;
            psym->rectyp = S_ALIGN;

            pgsT->Next = pgs;
            if ( pgsPrev ) {
                pgsPrev->Next = pgsT;
            }
            else {
                HT [ ipgs ] = pgsT;
            }

            offExtra += cbSym;
        }
    }

    return offExtra;
}