//
// bsc.c -- manage queries on the database
//
//	Copyright <C> 1988, Microsoft Corporation
//
// Revision History:
//
//

#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#define LINT_ARGS
#if defined(OS2)
#define INCL_NOCOMMON
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSFILEMGR
#define INCL_DOSERRORS
#define INCL_DOSMISC
#include <os2.h>
#include <dos.h>
#else
#include <windows.h>
#endif



#include "hungary.h"
#include "mbrcache.h"
#include "version.h"
#include "sbrbsc.h"
#include "bsc.h"

#define LISTALLOC 50		// Browser max list size

// static data

static FILEHANDLE       fhBSC = (FILEHANDLE)(-1);             // .BSC file handle

static BYTE		fCase;			// TRUE for case compare
static BYTE		MaxSymLen;		// longest symbol length
static WORD		ModCnt; 		// count of modules

static ISYM 		Unknown;		// UNKNOWN symbol index

static WORD	 	ModSymCnt;		// count of modsyms
static WORD 		SymCnt; 		// count of symbols
static WORD 		PropCnt;		// count of properties
static DWORD 		RefCnt; 		// count of references
static WORD 		DefCnt; 		// count of definitions
static WORD 		CalCnt; 		// count of calls
static WORD 		CbyCnt; 		// count of called bys
static WORD 		lastAtomPage;		// last atom page #
static WORD 		lastAtomCnt;		// last atom page size

static WORD 		cbModSymCnt;		// size of list of modsyms
static WORD 		cbSymCnt;		// size of list of symbols
static WORD 		cbPropCnt;		// size of list of properties
static WORD 		cbRefCnt;		// size of list of references
static WORD 		cbDefCnt;		// size of list of definitions
static WORD 		cbCalCnt;		// size of list of calls
static WORD 		cbCbyCnt;		// size of list of called bys

static WORD 		MaxModSymCnt;		// max list of modsyms
static WORD 		MaxSymCnt;		// max list of symbols
static WORD 		MaxPropCnt;		// max list of properties
static WORD 		MaxRefCnt;		// max list of references
static WORD 		MaxDefCnt;		// max list of references
static WORD 		MaxCalCnt;		// max list of calls
static WORD 		MaxCbyCnt;		// max list of called bys

static DWORD		lbModSymList;		// modsym    list file start
static DWORD		lbSymList;		// symbol    list file start
static DWORD		lbPropList;		// property  list file start
static DWORD		lbRefList;		// reference list file start
static DWORD		lbDefList;		// def'n     list file start
static DWORD		lbCalList;		// calls     list file start
static DWORD		lbCbyList;		// call bys  list file start
static DWORD		lbSbrList;		// sbr       list file start
static DWORD		lbAtomCache;		// atom     cache file start

static WORD		CurModSymPage  = 0;	// Current page of modsyms
static WORD		CurSymPage     = 0;	// Current page of symbols
static WORD		CurPropPage    = 0;	// Current page of properties
static WORD		CurRefPage     = 0;	// Current page of references
static WORD		CurDefPage     = 0;	// Current page of definitions
static WORD		CurCalPage     = 0;	// Current page of calls
static WORD		CurCbyPage     = 0;	// Current page of called bys

static LSZ		lszBSCName     = NULL;	// name of .bsc file

static MODLIST	   far	*pfModList     = NULL;	// module    list cache start
static MODSYMLIST  far	*pfModSymList  = NULL;	// modsym    list cache start
static SYMLIST	   far	*pfSymList     = NULL;	// symbol    list cache start
static PROPLIST    far	*pfPropList    = NULL;	// property  list cache start
static REFLIST	   far	*pfRefList     = NULL;	// reference list cache start
static REFLIST	   far	*pfDefList     = NULL;	// def'n     list cache start
static USELIST	   far	*pfCalList     = NULL;	// calls     list cache start
static USELIST	   far	*pfCbyList     = NULL;	// call bys  list cache start

static WORD		AtomPageTblMac = 0;		// last cache page used
static CACHEPAGE	AtomPageTbl[MAXATOMPAGETBL];	// Atom Cache table

#define bMOD(imod)	(pfModList[imod])
#define bMODSYM(isym)   (pfModSymList[isym])
#define bSYM(isym)      (pfSymList[isym])
#define bPROP(iprop)    (pfPropList[iprop])

#define bREF(iref)      (pfRefList[iref])
#define bDEF(idef)      (pfDefList[idef])

#define bCAL(iuse)      (pfCalList[iuse])
#define bCBY(iuse)      (pfCbyList[iuse])
#define bUSE(iuse)      (pfCalList[iuse])
#define bUBY(iuse)      (pfCbyList[iuse])

// prototypes
//

#define BSCIn(v) ReadBSC(&v, sizeof(v));

static VOID	GetBSC (DWORD lpos, LPV lpv, WORD cb);
static VOID	ReadBSC(LPV lpv, WORD cb);
static WORD	SwapPAGE (DWORD, LPV, WORD, WORD, WORD *, DWORD);
static LPCH	GetAtomCache (WORD);

static VOID
ReadBSC(LPV lpv, WORD cb)
// read a block of data from the BSC file
//
{
    if (BSCRead(fhBSC, lpv, cb) != cb)
	ReadError(lszBSCName);
}

static VOID
GetBSC(DWORD lpos, LPV lpv, WORD cb)
// Read a block of the specified size from the specified position
//
{
#if defined (OS2)
    if (BSCSeek(fhBSC, lpos, SEEK_SET) == -1)
#else
    if (BSCSeek(fhBSC, lpos, FILE_BEGIN) == -1)
        SeekError(lszBSCName);
#endif

    if (BSCRead(fhBSC, lpv, cb) != cb)
	ReadError(lszBSCName);
}

static WORD
SwapPAGE (DWORD lbuflist, LPV pfTABLE, WORD tblsiz,
/* */      WORD lstsiz, WORD * pcurpage, DWORD idx)
//
//
// SwapPAGE -	Swap in the table page for the table pfTABLE[idx]
//		and return the table's new index in the page.
{
    WORD page;
    WORD newidx;

    page   = (WORD)(idx / lstsiz);
    newidx = (WORD)(idx % lstsiz);

    if (page == *pcurpage)
	return newidx;

    GetBSC(lbuflist+((long)tblsiz*(long)page), pfTABLE, tblsiz);

    *pcurpage = page;
    return newidx;
}

static WORD
ModSymPAGE (IMOD imod)
// Swap in the ModSym page for ModSym[imod]
// return the ModSym's index in the page.
//
{
    return SwapPAGE (lbModSymList, pfModSymList,
	cbModSymCnt, MaxModSymCnt, &CurModSymPage, (IDX)imod);
}

static WORD
SymPAGE (ISYM isym)
// Swap in the Symbol page for symbol[isym]
// return the Symbol's index in the page.
//
{
    return SwapPAGE (lbSymList, pfSymList,
	cbSymCnt, MaxSymCnt, &CurSymPage, (IDX)isym);
}

static WORD
PropPAGE (IINST iinst)
// Swap in the Property page for Property[idx]
// return the Property's index in the page.
//
{
    return SwapPAGE (lbPropList, pfPropList,
	cbPropCnt, MaxPropCnt, &CurPropPage, (IDX)iinst);
}

static WORD
RefPAGE (IREF iref)
// Swap in the Reference page for Reference[idx] 
// return the Reference's index in the page.
//
{
    return SwapPAGE (lbRefList, pfRefList,
	cbRefCnt, MaxRefCnt, &CurRefPage, (IDX)iref);
}

static WORD
DefPAGE (IDEF idef)
// Swap in the Deference page for Definition[idef]  
// return the Definitions index in the page.
//
{
    return SwapPAGE (lbDefList, pfDefList,
	cbDefCnt, MaxDefCnt, &CurDefPage, (IDX)idef);
}

static WORD
CalPAGE (IUSE iuse)
// Swap in the Usage page for Usage[iuse]  (cal/cby)
// and return the Usage's index in the page.
//
{
    return SwapPAGE (lbCalList, pfCalList,
	cbCalCnt, MaxCalCnt, &CurCalPage, (IDX)iuse);
}

static WORD
CbyPAGE (IUSE iuse)
// Swap in the Usage page for Usage[iuse]  (cal/cby)
// and return the Usage's index in the page.
//
{
    return SwapPAGE (lbCbyList, pfCbyList,
	cbCbyCnt, MaxCbyCnt, &CurCbyPage, (IDX)iuse);
}

static LPCH
GetAtomCache (WORD Page)
// load the requested page into the cache
//
{
    register	WORD ipg;
    WORD	pagesize;
    LPCH 	pfAtomCsave;

    for (ipg = 0; ipg < AtomPageTblMac; ipg++) {
	if (AtomPageTbl[ipg].uPage == Page)
	    return AtomPageTbl[ipg].pfAtomCache;
    }

    if (ipg != MAXATOMPAGETBL) {
	if (AtomPageTbl[ipg].pfAtomCache ||
	   (AtomPageTbl[ipg].pfAtomCache = LpvAllocCb(ATOMALLOC)))
		AtomPageTblMac++;
    }

    pfAtomCsave = AtomPageTbl[AtomPageTblMac-1].pfAtomCache;

    for (ipg = AtomPageTblMac-1; ipg; ipg--)
	AtomPageTbl[ipg] = AtomPageTbl[ipg-1];		// move up

    AtomPageTbl[0].pfAtomCache = pfAtomCsave;
    AtomPageTbl[0].uPage = Page;

    if (Page == lastAtomPage)
	pagesize = lastAtomCnt;
    else
	pagesize = ATOMALLOC;

    GetBSC(lbAtomCache+ATOMALLOC*(long)Page,
		AtomPageTbl[0].pfAtomCache, pagesize);

    return AtomPageTbl[0].pfAtomCache;
}

LSZ BSC_API
LszNameFrSym (ISYM isym)
// Swap in the Atom page for the symbol isym
// return the atom's address in the page.
//
{
    SYMLIST sym;

    sym = bSYM(isym);
    return GetAtomCache (sym.Page) + sym.Atom;
}

LSZ BSC_API
LszNameFrMod (IMOD imod)
// Swap in the Atom page for the module isym
// return the atom's address in the page.
//
{
   return LszNameFrSym(bMOD(imod).ModName);
}

int BSC_API
CaseCmp(LSZ lsz1, LSZ lsz2)
//
// think of lsz1 and lsz2 being in a list of things that are sorted
// case insensitively and then case sensitively within that.  This is
// the case for browser symbols
//
// return -1, 0, or 1 if lsz1 before, at, or after lsz2 in the list
//
{
    int ret;

    // do case insensitive compare
    ret = _stricmp(lsz1, lsz2);

    // if this is good enough then use it, or if we are only doing
    // a case insensitive search then this is good enough

    if (ret || !fCase) return ret;

    // if we must, do the case sensitive compare

    return strcmp(lsz1, lsz2);
}


ISYM BSC_API
IsymFrLsz (LSZ lszReqd)
// find the symbol with the specifed name
//
{
    ISYM  Lo, Hi, Mid;
    int  Cmp;
    LSZ	 lszCur;

    Lo = 0;
    Hi = (ISYM)(SymCnt - 1);

    while (Lo <= Hi) {
        Mid = (ISYM)((Hi + Lo) / 2);

	lszCur = LszNameFrSym (Mid);
	Cmp = CaseCmp (lszReqd, lszCur);

	if (Cmp == 0)
	    return Mid;
	    
	if (Cmp < 0)
            Hi = (ISYM)(Mid - 1);
	else
            Lo = (ISYM)(Mid + 1);
	}
    return isymNil;
}

IMOD BSC_API
ImodFrLsz (LSZ lszReqd)
// find the module with the specifed name
//
{
    IMOD imod;

    for (imod = 0; imod < ModCnt; imod++) {
        if (_stricmp (lszReqd, LszNameFrSym (bMOD(imod).ModName)) == 0)
	    return imod;
	}

    return imodNil;
}

ISYM BSC_API
IsymMac()
// return the biggest isym in this database
//
{
   return SymCnt;
}

IMOD BSC_API
ImodMac()
// return the biggest imod in this database
//
{
   return ModCnt;
}

IINST BSC_API
IinstMac()
// return the biggest iinst in this database
//
{
   return PropCnt;
}

VOID BSC_API
MsRangeOfMod(IMOD imod, IMS *pimsFirst, IMS *pimsLast)
// fill in the module information
//
{
   *pimsFirst = imod ? bMOD(imod-1).mSymEnd : 0;
   *pimsLast  = bMOD(imod).mSymEnd;
}

IINST BSC_API
IinstOfIms(IMS ims)
// give the instance (PROP) index of the modsym
//
{
   return bMODSYM(ims).ModSymProp;
}

VOID BSC_API
InstRangeOfSym(ISYM isym, IINST *piinstFirst, IINST *piinstLast)
// fill in the range of inst values for this symbol
//
{
   *piinstFirst = isym ? bSYM(isym-1).PropEnd:0;
   *piinstLast  = bSYM(isym).PropEnd;
}

VOID BSC_API
InstInfo(IINST iinst, ISYM *pisymInst, TYP *pTyp, ATR *pAtr)
// get the information that qualifies this instance
//
{
   *pisymInst  = bPROP(iinst).PropName;
   *pAtr       = bPROP(iinst).PropAttr & 0x3ff;
   *pTyp       = (bPROP(iinst).PropAttr >> 11) & 0x1f;
}

VOID BSC_API
RefRangeOfInst(IINST iinst, IREF *pirefFirst, IREF *pirefLast)
// fill in the reference ranges from the inst
//
{
   *pirefFirst = iinst ? bPROP(iinst-1).RefEnd : 0;
   *pirefLast  = bPROP(iinst).RefEnd;
}

VOID BSC_API
DefRangeOfInst(IINST iinst, IDEF *pidefFirst, IDEF *pidefLast)
// fill in the definition ranges from the inst
//
{
   *pidefFirst = iinst ? bPROP(iinst-1).DefEnd : 0;
   *pidefLast  = bPROP(iinst).DefEnd;
}

VOID BSC_API
UseRangeOfInst(IINST iinst, IUSE *piuseFirst, IUSE *piuseLast)
// fill in the use ranges from the inst
//
{
   *piuseFirst = iinst ? bPROP(iinst-1).CalEnd : 0;
   *piuseLast  = bPROP(iinst).CalEnd;
}

VOID BSC_API
UbyRangeOfInst(IINST iinst, IUBY *piubyFirst, IUBY *piubyLast)
// fill in the used by ranges from the inst
//
{
   *piubyFirst = iinst ? bPROP(iinst-1).CbyEnd : 0;
   *piubyLast  = bPROP(iinst).CbyEnd;
}

VOID BSC_API
UseInfo(IUSE iuse, IINST *piinst, WORD *pcnt)
// fill in the information about this things which an inst uses
//
{
   *piinst = bUSE(iuse).UseProp;
   *pcnt   = bUSE(iuse).UseCnt;
}

VOID BSC_API
UbyInfo(IUBY iuby, IINST *piinst, WORD *pcnt)
// fill in the information about this things which an inst is used by
//
{
   *piinst = bUBY(iuby).UseProp;
   *pcnt   = bUBY(iuby).UseCnt;
}

VOID BSC_API
RefInfo(IREF iref, LSZ *plszName, WORD *pline)
// fill in the information about this reference
//
{
   *pline    = bREF(iref).RefLin;
   *plszName = LszNameFrSym(bREF(iref).RefNam);
}

VOID BSC_API
DefInfo(IDEF idef, LSZ *plszName, WORD *pline)
// fill in the information about this definition
//
{
   *pline    = bDEF(idef).RefLin;
   *plszName = LszNameFrSym(bDEF(idef).RefNam);
}

VOID BSC_API
CloseBSC()
// close database and free as much memory as possible
//
{
    int i;

    // close file if open

    if (fhBSC != (FILEHANDLE)(-1)) {
	BSCClose (fhBSC);
        fhBSC = (FILEHANDLE)(-1);
    }

    // free any memory we may have allocated

    if (pfModList)    { FreeLpv (pfModList);	pfModList    = NULL; }
    if (pfModSymList) { FreeLpv (pfModSymList); pfModSymList = NULL; }
    if (pfSymList)    { FreeLpv (pfSymList);	pfSymList    = NULL; }
    if (pfPropList)   { FreeLpv (pfPropList);	pfPropList   = NULL; }
    if (pfRefList)    { FreeLpv (pfRefList);	pfRefList    = NULL; }
    if (pfDefList)    { FreeLpv (pfDefList);	pfDefList    = NULL; }
    if (pfCalList)    { FreeLpv (pfCalList);	pfCalList    = NULL; }
    if (pfCbyList)    { FreeLpv (pfCbyList);	pfCbyList    = NULL; }

    for (i=0; i < MAXATOMPAGETBL; i++) {
	if (AtomPageTbl[i].pfAtomCache) {
	    FreeLpv (AtomPageTbl[i].pfAtomCache);  // dispose Atom Cache
	    AtomPageTbl[i].pfAtomCache = NULL;
	}
    }
}

BOOL BSC_API
FOpenBSC (LSZ lszName)
//  Open the specified data base.
//  Allocate buffers for cache areas
//  Initialize the data cache from the data base.
//
//  Return TRUE iff successful, FALSE if database can't be read
//
{
    WORD	pagesize;

    BYTE	MajorVer;		// .bsc version (major)
    BYTE	MinorVer;		// .bsc version (minor)
    BYTE	UpdatVer;		// .bsc version (updat)

    WORD	MaxModCnt;		// max list of modules
    WORD	cbModCnt;		// size of list of modules
    DWORD	lbModList;		// module  list file start

    int 	i;

    #define ABORT_OPEN	CloseBSC(); return FALSE;

    lszBSCName = lszName;

#if defined (OS2)
    fhBSC = BSCOpen(lszBSCName, O_BINARY|O_RDONLY);
#else
    fhBSC = BSCOpen(lszBSCName, GENERIC_READ);
#endif

    // if the .bsc file doesn't exist then we don't do any work
    // this is the cold compile case
    //

    if (fhBSC == (FILEHANDLE)(-1)) {ABORT_OPEN;}

    // read and check BSC version (major, minor and update)

    BSCIn(MajorVer);
    BSCIn(MinorVer);
    BSCIn(UpdatVer);

    BSCPrintf("Browser Data Base: %s ver %d.%d.%d\n\n",
	 lszBSCName, MajorVer, MinorVer, UpdatVer);

    if ((MajorVer !=  BSC_MAJ) ||
	(MinorVer !=  BSC_MIN) ||
	(UpdatVer !=  BSC_UPD)) {

	    CloseBSC();
	    BadBSCVer(lszBSCName);
	    return FALSE;
	}

    // read Case sense switch, max symbol length and Unknown module id

    BSCIn(fCase);
    BSCIn(MaxSymLen);
    BSCIn(Unknown);

    // this will make the formatting look more reasonable if there are
    // only very short names in the database

    if (MaxSymLen < 8 ) MaxSymLen = 8;

    // read counts (sizes) of each data area

    BSCIn(ModCnt);
    BSCIn(ModSymCnt);
    BSCIn(SymCnt);
    BSCIn(PropCnt);
    BSCIn(RefCnt);
    BSCIn(DefCnt);
    BSCIn(CalCnt);
    BSCIn(CbyCnt);
    BSCIn(lastAtomPage);
    BSCIn(lastAtomCnt);

    // read BSC data area offsets

    BSCIn(lbModList);
    BSCIn(lbModSymList);
    BSCIn(lbSymList);
    BSCIn(lbPropList);
    BSCIn(lbRefList);
    BSCIn(lbDefList);
    BSCIn(lbCalList);
    BSCIn(lbCbyList);
    BSCIn(lbAtomCache);
    BSCIn(lbSbrList);

    // determine data cache area sizes

    #define MIN(a,b) ((a)>(b) ? (b) : (a))

    MaxModCnt    = ModCnt;              // max list of modules
    MaxModSymCnt = ModSymCnt;           // max list of modsyms
    MaxSymCnt    = SymCnt+ModCnt;       // max list of symbols
    MaxPropCnt   = PropCnt;             // max list of props
    MaxRefCnt    = RefCnt;              // max list of refs
    MaxDefCnt    = DefCnt;              // max list of defs
    MaxCalCnt    = CalCnt;              // max list of cals
    MaxCbyCnt    = CbyCnt;              // max list of cbys

    cbModCnt	 = sizeof(MODLIST)    * MaxModCnt;	// size of mods list
    cbModSymCnt  = sizeof(MODSYMLIST) * MaxModSymCnt;	// size of modsyms list
    cbSymCnt	 = sizeof(SYMLIST)    * MaxSymCnt;	// size of syms list
    cbPropCnt	 = sizeof(PROPLIST)   * MaxPropCnt;	// size of props list
    cbRefCnt	 = sizeof(REFLIST)    * MaxRefCnt;	// size of refs list
    cbDefCnt	 = sizeof(REFLIST)    * MaxDefCnt;	// size of defs list
    cbCalCnt	 = sizeof(USELIST)    * MaxCalCnt;	// size of cals list
    cbCbyCnt	 = sizeof(USELIST)    * MaxCbyCnt;	// size of cbys list

    // Allocate buffers for each of the object types

    if (!(pfModList	= LpvAllocCb(cbModCnt)))	{ ABORT_OPEN; }
    if (!(pfModSymList	= LpvAllocCb(cbModSymCnt)))	{ ABORT_OPEN; }
    if (!(pfSymList	= LpvAllocCb(cbSymCnt)))	{ ABORT_OPEN; }
    if (!(pfPropList	= LpvAllocCb(cbPropCnt)))	{ ABORT_OPEN; }
    if (!(pfRefList	= LpvAllocCb(cbRefCnt)))	{ ABORT_OPEN; }
    if (!(pfDefList	= LpvAllocCb(cbDefCnt)))	{ ABORT_OPEN; }
    if (!(pfCalList	= LpvAllocCb(cbCalCnt)))	{ ABORT_OPEN; }
    if (!(pfCbyList	= LpvAllocCb(cbCbyCnt)))	{ ABORT_OPEN; }

    // read data areas

    if (lastAtomPage == 0)
	pagesize = lastAtomCnt;
    else
	pagesize = ATOMALLOC;

    // clear out the atom cache
    // we must be able to allocate at least one page

    AtomPageTblMac = 0;

    for (i=0; i < MAXATOMPAGETBL; i++)
	AtomPageTbl[i].pfAtomCache = NULL;

    AtomPageTbl[0].uPage = 65535;
    AtomPageTbl[0].pfAtomCache = LpvAllocCb(pagesize);
    if (!AtomPageTbl[0].pfAtomCache) { ABORT_OPEN; }


    GetBSC(lbModList,    pfModList,    cbModCnt);    // Init Mod    cache
    GetBSC(lbModSymList, pfModSymList, cbModSymCnt); // Init ModSym cache
    GetBSC(lbSymList,    pfSymList,    cbSymCnt);    // Init Sym    cache
    GetBSC(lbPropList,   pfPropList,   cbPropCnt);   // Init Prop   cache
    GetBSC(lbRefList,    pfRefList,    cbRefCnt);    // Init Ref    cache
    GetBSC(lbDefList,    pfDefList,    cbDefCnt);    // Init Def    cache
    GetBSC(lbCalList,    pfCalList,    cbCalCnt);    // Init Cal    cache
    GetBSC(lbCbyList,    pfCbyList,    cbCbyCnt);    // Init Cby    cache

    // current page for all database items is now page zero

    CurModSymPage = 0;
    CurSymPage    = 0;
    CurPropPage   = 0;
    CurRefPage    = 0;
    CurDefPage    = 0;
    CurCalPage    = 0;
    CurCbyPage    = 0;

    GetAtomCache (0);  // Init Atom cache

    return TRUE;
}

WORD BSC_API
BSCMaxSymLen()
// return the length of the largest symbol in the database
//
{
    return MaxSymLen;
}

BOOL BSC_API
FCaseBSC()
// is this database built with a case sensitive language?
//
{
   return fCase;
}

VOID BSC_API
SetCaseBSC(BOOL fNewCase)
// set case sensitivity of database
//
{
   fCase = (BYTE)!!fNewCase;
}