//
// ADDTOLST.C - Add each record from the .SBR file to the approprate list.
//

#define LINT_ARGS

#include "sbrfdef.h"
#include "mbrmake.h"
#include <ctype.h>

// local types

typedef struct _mstk {
	struct  _mstk FAR *pmstkPrev;		// next module stack entry	
	VA	vaCurMod;			// saved current module 
	BOOL	fDupMod;			// saved dup module flag
	BOOL	fExclMod;			// saved exclude module flag
} MSTK, FAR * PMSTK;

typedef struct _bstk {
	struct _bstk FAR *pbstkPrev;		// next block stack entry
	VA 	vaOwnerProp;			// saved current owner
} BSTK, FAR * PBSTK;

// static variables

BOOL	near fDupSym	 = FALSE;		// TRUE if adding duplicate atom
BOOL	near cMacroDepth = 0;			// depth of MACROBEG records
WORD	near ModCnt;				// count of modules
WORD	near isbrCur;				// current SBR file index

VA	near vaUnknownSym = vaNil;		// Unknown symbol
VA	near vaUnknownMod = vaNil;		// Unknown module

static VA   near vaOwnerProp = vaNil;		// ptr to current procedure 
static BOOL near fDupMod   = FALSE;		// duplicate module
static BOOL near fExclMod  = FALSE;		// exclude this module
static BOOL near fFirstMod = TRUE;		// this is 1st module of file

static PMSTK pmstkRoot;				// root of module stack
static PBSTK pbstkRoot;				// root of block stack

// forward references

static BOOL FSkipMacro(void);
static VOID PushMstk(VOID);
static VOID PushBstk(VOID);
static VOID PopMstk(VOID);
static VOID PopBstk(VOID);
static VOID CheckStacksEmpty(VOID);

VOID
SBRCorrupt (char *psz)
// sbr file corrupt -- print message
//
{

#ifdef DEBUG
    printf("Info = %s\n", psz);
#else
    // to make /W3 happy
    psz;
#endif

    Error(ERR_SBR_CORRUPT, lszFName);
}

static VOID
PushMstk (VOID)
// stack the current module context -- occurs before SBR_REC_MODULE 
//
{
    PMSTK pmstk;

    pmstk = LpvAllocCb(sizeof(*pmstk));

    pmstk->vaCurMod	 = vaCurMod;		// current module
    pmstk->fDupMod	 = fDupMod;		// dup	   module
    pmstk->fExclMod	 = fExclMod;		// exclude module
    pmstk->pmstkPrev     = pmstkRoot;		// make stack links
    pmstkRoot            = pmstk;		// root <- new
}

static VOID
PushBstk (VOID)
// stack the current block context -- occurs before SBR_REC_BLKBEG
//
{
    PBSTK pbstk;

    pbstk = LpvAllocCb(sizeof(*pbstk));

    pbstk->vaOwnerProp	 = vaOwnerProp;		// current owner
    pbstk->pbstkPrev     = pbstkRoot;		// make stack links
    pbstkRoot            = pbstk;		// root <- new
}

static VOID
PopMstk (VOID)
// restore previous module context -- occurs on SBR_REC_MODEND
//
{
    PMSTK pmstk;

    if (pmstkRoot == NULL) {
#ifdef DEBUG
	SBRCorrupt("Module stack empty but MODEND was found");
#else
	SBRCorrupt("");
#endif
    }

    vaCurMod	  = pmstkRoot->vaCurMod;      // get previous current module
    fDupMod	  = pmstkRoot->fDupMod;       // get previous dup mod flag
    fExclMod	  = pmstkRoot->fExclMod;      // get previous excl mod flag

    pmstk         = pmstkRoot;
    pmstkRoot     = pmstkRoot->pmstkPrev;

    FreeLpv(pmstk);
}

static VOID
PopBstk (VOID)
// restore previous block context -- occurs on SBR_REC_BLKEND
//
{
    PBSTK pbstk;

    if (pbstkRoot == NULL) {
#ifdef DEBUG
	SBRCorrupt("Block stack empty but BLKEND was found");
#else
	SBRCorrupt("");
#endif
    }

    vaOwnerProp   = pbstkRoot->vaOwnerProp;    // get previous current proc

    pbstk         = pbstkRoot;
    pbstkRoot     = pbstkRoot->pbstkPrev;

    FreeLpv(pbstk);
}

static VOID
CheckStacksEmpty(VOID)
// check to make sure that both stacks are empty at the .sbr file EOF
//
{
    if (pmstkRoot != NULL) {
#ifdef DEBUG
	SBRCorrupt("Module stack not empty at EOF");
#else
	SBRCorrupt("");
#endif
    }

    if (pbstkRoot != NULL) {
#ifdef DEBUG
	SBRCorrupt("Block stack not empty at EOF");
#else
	SBRCorrupt("");
#endif
    }
}

BOOL
FInExcList (LSZ lszName)
// Is the module name in the exclude file list?
//	
{
    EXCLINK FAR * px;
    LSZ lszAbs;

    if (OptEs && !fFirstMod) {
	if (lszName[0] == '\0') return FALSE;

	if (lszName[0] == '/' || lszName[0] == '\\') return TRUE;
	if (lszName[1] == ':' && lszName[2] == '/') return TRUE;
	if (lszName[1] == ':' && lszName[2] == '\\') return TRUE;
    }

    px = pExcludeFileList;

    // this name is relative to the path given in the header file
    lszAbs = ToAbsPath(lszName, r_cwd);

    while (px) {
	if ((FWildMatch (px->pxfname, lszAbs)))
	    return TRUE;
	px = px->xnext;
    }
    return FALSE;
}

static BOOL
FSkipMacro()
// return TRUE if this record should be skipped given that we are inside
// of a macro definition  (i.e cMacroDepth is known to be non-zero)
//
{
    if (!OptEm)
	return FALSE;

    if ((r_rectyp == SBR_REC_BLKBEG) ||
	(r_rectyp == SBR_REC_BLKEND) ||
	(r_rectyp == SBR_REC_MACROEND))
	    return FALSE;

    return TRUE;
}

VOID
InstallSBR()
//  Read the next .sbr file and add all the defs/refs/cals/cbys etc to
//  the various lists
//
{
    WORD   nlen;

    VA vaCurSym;		// current   symbol
    VA vaProp;			// current property
    VA vaOrd;			// current property temp   

    BOOL fSymSet = FALSE;	// TRUE if symbol set reference 

    r_lineno = 0;

    fExclMod = FALSE;
    fFirstMod = TRUE;		// we haven't seen the first MODULE record yet

    vaUnknownSym = MbrAddAtom ("<Unknown>", TRUE);  // unknown module name

    if (vaUnknownMod == vaNil) {

	vaUnknownMod = VaAllocGrpCb(grpMod, sizeof(MOD));

	vaCurMod = vaUnknownMod;

	gMOD(vaCurMod).csyms = 0;
	cMOD.vaNameSym	= vaUnknownSym;
	pMOD(vaCurMod);

	gSYM(vaUnknownSym).vaFirstProp = vaCurMod; // store pointer back to MOD
	pSYM(vaUnknownSym);
	ModCnt++;
    }
    else
	fDupMod = (vaUnknownMod != 0);

    vaCurMod = vaUnknownMod;

    if (vaRootMod == vaNil)
	vaRootMod = vaCurMod;

    while (GetSBRRec() != S_EOF) {

	#ifdef DEBUG
	if (OptD & 1) DecodeSBR ();
	#endif

	if (cMacroDepth != 0) 	// skip SYMBOLS in macros if true
	    if (FSkipMacro ())
		continue;

	if (fExclMod) {
	    if ((r_rectyp == SBR_REC_MODULE) ||
		(r_rectyp == SBR_REC_SYMDEF) ||
		(r_rectyp == SBR_REC_MODEND)) {
			;
	    }
	    else
		continue;
	}

	switch(r_rectyp) {

	case SBR_REC_MODULE:
	    PushMstk ();		// save state

	    r_lineno = 0;		// reset line no.

	    fDupMod  = FALSE;		// go to a known state
	    fExclMod = FALSE;

	    if (fExclMod = FInExcList (r_bname)) {
		#ifdef DEBUG
		  if (OptD & 256)
			printf ("  module excluded = %s\n", r_bname);
		#endif
		vaCurMod = vaUnknownMod;
	    }
	    else if ((vaCurMod = VaSearchModule (r_bname)) != vaNil) {
		if (gMOD(vaCurMod).csyms == 0) {
		    fDupMod = TRUE;
		    #ifdef DEBUG
		    if (OptD & 256)
		        printf ("  module redef = %s\n", r_bname);
		    #endif
		}
		else {
		    cMOD.csyms = 0;
		    pMOD(vaCurMod);

		    #ifdef DEBUG
		    if (OptD & 256)
		        printf ("  module subst = %s\n", r_bname);
		    #endif
		}
	    }
	    else {
		SetVMClient(VM_ADD_MOD);
		ModCnt++;
		vaCurMod	   = VaAllocGrpCb(grpMod, sizeof(MOD));
		gMOD(vaCurMod);
		cMOD.vaFirstModSym = vaNil;
		cMOD.csyms	   = 0;
		cMOD.vaNameSym	   =
                        MbrAddAtom (ToCanonPath(r_bname, r_cwd, c_cwd), TRUE);
		cMOD.vaNextMod	   = vaRootMod;
		pMOD(vaCurMod);

		vaRootMod	   = vaCurMod;

		gSYM(cMOD.vaNameSym).vaFirstProp = vaCurMod; // ptr to MOD
		pSYM(cMOD.vaNameSym);

		SetVMClient(VM_MISC);
	    }

	    fFirstMod = FALSE;
	    break;

	case SBR_REC_LINDEF:
	    break;

	case SBR_REC_SYMDEF:

	    // if module is being excluded then just make the ord and prop entry
	    // in case it is referred to later.

	    // REVIEW  For FORTRAN if ordinal is already defined
	    // REVIEW  then this is a refined definition -- we
	    // REVIEW  override the old definition with the new
	    // REVIEW  one at this time	-Rico

	    nlen = strlen (r_bname);
	    if (nlen > MaxSymLen) MaxSymLen = (BYTE)nlen;

            vaCurSym = MbrAddAtom (r_bname, FALSE);
	    vaOrd    = VaOrdAdd ();	// add sym ord to ord list
	    gORD(vaOrd).vaOrdProp = VaPropAddToSym(vaCurSym);
	    pORD(vaOrd);

	    break;

	case SBR_REC_OWNER:
	    if (!(vaProp = VaOrdFind(r_ordinal))) {
		// emit error message in case of forward reference
		// try to continue
		//
		#ifdef DEBUG
		if (OptD & 4)
                    printf ("mbrmake: Owner Forward Reference(%d)\n",
				r_ordinal); 
		#endif
		break;
	    }
	    vaOwnerProp = vaProp;
	    break;

	case SBR_REC_SYMREFSET:
	    fSymSet = TRUE;
	    // fall through

	case SBR_REC_SYMREFUSE:

	    if (!(vaProp = VaOrdFind(r_ordinal))) {
		// emit error message in case of forward reference
		// try to continue
		//
		#ifdef DEBUG
		if (OptD & 4)
                    printf ("mbrmake: Forward Reference(%d)\n", r_ordinal);
		#endif
		break;
	    }

	    AddRefProp (vaProp);
	    break;

	case SBR_REC_BLKBEG:
	    PushBstk();			// save state
	    break;

	case SBR_REC_MACROBEG:
	    cMacroDepth++;
	    break;

	case SBR_REC_MACROEND:
	    cMacroDepth--;
	    break;

	case SBR_REC_BLKEND:
	    PopBstk();
	    break;

	case SBR_REC_MODEND:
	    PopMstk();
	    break;

	default:
	    SBRCorrupt ("unknown rec type");
	    Fatal ();
	    break;

	}
    }

    CheckStacksEmpty();
}

VOID
AddCalProp(VA vaCurProp)
// Add a symbol reference to the calling procedure's Calls/Uses list.
//
{
    CAL cal;

    SetVMClient(VM_SEARCH_CAL);

    ENM_LIST (gPROP(vaOwnerProp).vaCalList, CAL)	// proc call list

	if (cCAL.vaCalProp == vaCurProp) {
	    cCAL.isbr = isbrCur;
	    cCAL.calcnt++;			// multiple calls
	    ENM_PUT(CAL);
	    return;
	}

    ENM_END

    cal.isbr	  = isbrCur;
    cal.vaCalProp = vaCurProp;			// symbol called or used
    cal.calcnt    = 1;

    SetVMClient(VM_ADD_CAL);

    VaAddList(&cPROP.vaCalList, &cal, sizeof(cal), grpCal);
	
    pPROP(vaOwnerProp);

    SetVMClient(VM_MISC);

#ifdef DEBUG
    if (OptD & 8) {
    	printf("New CAL for: ");
	DebugDumpProp(vaOwnerProp);
    }
#endif
}

VOID
AddCbyProp(VA vaCurProp)
// Add a symbol reference to it's property Called/Used by list.
//
{
    CBY cby;

    SetVMClient(VM_SEARCH_CBY);

    ENM_LIST (gPROP(vaCurProp).vaCbyList, CBY)  // prop called/used by list 

	if (cCBY.vaCbyProp == vaOwnerProp) {
	    cCBY.isbr = isbrCur;
	    cCBY.cbycnt++;
	    ENM_PUT(CBY);
	    return;
	}

    ENM_END

    cby.isbr	  = isbrCur;
    cby.vaCbyProp = vaOwnerProp;   	// symbol we are called or used by 
    cby.cbycnt    = 1;

    SetVMClient(VM_ADD_CBY);

    VaAddList(&cPROP.vaCbyList, &cby, sizeof(cby), grpCby);

    pPROP(vaCurProp);

    SetVMClient(VM_MISC);

#ifdef DEBUG
    if (OptD & 8) {
    	printf("New CBY for: ");
	DebugDumpProp(vaCurProp);
    }
#endif
}

VOID
AddRefProp(VA vaCurProp)
// Add a symbol reference to it's property reference list.
//
{
    VA vaRef, vaFileSym;

    SetVMClient(VM_SEARCH_REF);

    vaFileSym = gMOD(vaCurMod).vaNameSym;

    gPROP(vaCurProp);

    if (fDupMod) {
	// try looking at the hint for this PROP if there is one, if there
	// isn't then we're stuck -- we must search the whole REF list
	//

	if (vaRef = cPROP.vaHintRef) {
	    gREF(vaRef);

	    if (cREF.reflin == r_lineno) {
		cREF.isbr = isbrCur;
		pREF(vaRef);
		SetVMClient(VM_MISC);
		return;
	    }

	    vaRef = VaFrVp(cREF.vpNextRef);
	    if (vaRef) {
		gREF(vaRef);
		if (cREF.reflin == r_lineno) {
		    cREF.isbr = isbrCur;
		    pREF(vaRef);
		    cPROP.vaHintRef = vaRef;
		    pPROP(vaCurProp);
		    SetVMClient(VM_MISC);
		    return;
		}
	    }
	}

	vaRef = VaFrVp(cPROP.vpFirstRef);

	while (vaRef) {
	    gREF(vaRef);
	    if ((VaFrVp(cREF.vpFileSym) == vaFileSym) && // ignore multiple
		(cREF.reflin == r_lineno)) {  // references to same file & line
		    cREF.isbr = isbrCur;
		    pREF(vaRef);
		    cPROP.vaHintRef = vaRef;
		    pPROP(vaCurProp);
		    SetVMClient(VM_MISC);
		    return;
	    }
	    vaRef = VaFrVp(cREF.vpNextRef);
	}
    }
    else {
	if (vaRef = VaFrVp(cPROP.vpLastRef)) {
	    gREF(vaRef);
	    if (cREF.reflin == r_lineno &&
		vaFileSym   == VaFrVp(cREF.vpFileSym)) {
		    SetVMClient(VM_MISC);
		    return;
		}
	}
    }

    SetVMClient(VM_ADD_REF);

    vaRef = VaAllocGrpCb(grpRef, sizeof(REF));

    gREF(vaRef);
    cREF.isbr		= isbrCur;
    cREF.reflin 	= r_lineno;

    MkVpVa(cREF.vpFileSym, vaFileSym);

    pREF(vaRef);

    gPROP(vaCurProp);

    AddTail (Ref, REF);

    cPROP.cref++;			// count references 
    cPROP.vaHintRef = vaRef;

    pPROP(vaCurProp);	

#ifdef DEBUG
    if (OptD & 8) {
    	printf("New REF for: ");
	DebugDumpProp(vaCurProp);
    }
#endif

    SetVMClient(VM_MISC);

    if (vaOwnerProp) {
	AddCbyProp (vaCurProp);		// add to called/used by 
	AddCalProp (vaCurProp);		// add to call/uses	 
    }
}

VOID
AddDefProp(VA vaCurProp)
// Add a symbol definition to it's property definition list.
// 	-Set vaOwnerProp if symbol is an internal function.
{
    DEF def;
    VA  vaFileSym;

#if 0

    // if current symbol is FUNCTION and formally declared
    // (block stack not empty), then remember it.
    // Subsequent symbols are called by or used by this function.
    //
    // this is going away when all compilers support SBR_REC_OWNER

    if ((r_attrib & SBR_TYPMASK) == SBR_TYP_FUNCTION)
	if (pfblkstack != NULL && !(r_attrib & SBR_ATR_DECL_ONLY))
	    vaOwnerProp = vaCurProp;
#endif

    vaFileSym = gMOD(vaCurMod).vaNameSym;

    ENM_LIST (gPROP(vaCurProp).vaDefList, DEF)	// proc def list

	if ((cDEF.vaFileSym == vaFileSym) && // ignore multiple
	    (cDEF.deflin    == r_lineno)) {  // references to same file & line
		cDEF.isbr = isbrCur;
		ENM_PUT(DEF);
		SetVMClient(VM_MISC);
		return;
	}

    ENM_END

    def.isbr		= isbrCur;
    def.deflin 		= r_lineno;
    def.vaFileSym 	= vaFileSym;

    SetVMClient(VM_ADD_DEF);

    gPROP(vaCurProp);

    VaAddList(&cPROP.vaDefList, &def, sizeof(def), grpDef);

    pPROP(vaCurProp);

    SetVMClient(VM_MISC);

#ifdef DEBUG
    if (OptD & 8) {
    	printf("New DEF for: ");
	DebugDumpProp(vaCurProp);
    }
#endif

    // don't count the definitions of the current proc as uses

    if (vaOwnerProp && vaCurProp != vaOwnerProp) {
	AddCbyProp (vaCurProp);		// add to called/used by 
	AddCalProp (vaCurProp);		// add to call/uses	 
    }
}


VA
VaPropBestOfSym(VA vaSym)
//
// Returns property pointer if:
//	1). symbol is already defined,
//	2). attributes match (except for possibly ATR_DECL_ONLY)
//
// Idea is to recognize the definition of an external.
//
{
    VA vaProp;
    WORD sattr;

    SetVMClient(VM_SEARCH_PROP);

    vaProp = gSYM(vaSym).vaFirstProp;

    while (vaProp) {
	sattr = gPROP(vaProp).sattr;

	if ((r_attrib & (~SBR_ATR_DECL_ONLY))
			== (sattr & (~SBR_ATR_DECL_ONLY))) {
    	    SetVMClient(VM_MISC);
	    return (vaProp);
	}

	vaProp = cPROP.vaNextProp;
    }

    SetVMClient(VM_MISC);

    return vaNil;
}

VA
VaPropAddToSym(VA vaCurSym)
// Add a property node for the given symbol.
//
{
    char fDupProp = FALSE;
    VA vaCurProp;

    if (vaCurProp = VaPropBestOfSym (vaCurSym)) {
	if ( (cPROP.sattr & SBR_ATR_DECL_ONLY) &&
	    !(r_attrib    & SBR_ATR_DECL_ONLY)) {
		cPROP.sattr = r_attrib;
		pPROP(vaCurProp);
	}
	fDupProp = TRUE;
    }
    else {
	SetVMClient(VM_ADD_PROP);

	vaCurProp = VaAllocGrpCb(grpProp, sizeof(PROP));
	gPROP(vaCurProp);
	cPROP.vaNameSym = vaCurSym;
	cPROP.sattr 	= r_attrib;

	if (gSYM(vaCurSym).vaFirstProp)
	    cPROP.vaNextProp = cSYM.vaFirstProp;

	pPROP(vaCurProp);

	cSYM.vaFirstProp = vaCurProp;
	cSYM.cprop++;
	pSYM(vaCurSym);

	SetVMClient(VM_MISC);
    }

    if (!fExclMod) {
	if (r_attrib & SBR_ATR_DECL_ONLY) 
	    AddRefProp (vaCurProp);		// treat extern as ref 
	else
	    AddDefProp (vaCurProp);		// define others
    }

    return (vaCurProp);
}

VOID
BldModSymList ()
// Build each module's symbol list
//
{
   WORD i;
   VA vaMod, vaModSym, vaSym, vaProp;

   SetVMClient(VM_BUILD_MODSYM);

   // zero out module symbol counts
   vaMod = vaRootMod;
   while (vaMod) {
      gMOD(vaMod);
      cMOD.csyms = 0;
      pMOD(vaMod);
      vaMod = cMOD.vaNextMod;
   }

   for (i=0; i < cSymbolsMac; i++) {
      vaSym = rgvaSymSorted[i];

      if (!vaSym) continue;

      vaProp = gSYM(vaSym).vaFirstProp;

      while (vaProp) {
	 ENM_LIST(gPROP(vaProp).vaDefList, DEF)

	    vaMod = vaRootMod;	   // look at defs for each mod */
	    while (vaMod) {
	       if (cDEF.vaFileSym == gMOD(vaMod).vaNameSym) {

		  if (cMOD.vaLastModSym  &&
		      gMODSYM(cMOD.vaLastModSym).vaFirstProp == vaProp)
			goto break2;  // duplicate

		  // belongs to this mod 
		  cMOD.csyms++;
		  
		  vaModSym = VaAllocGrpCb(grpModSym, sizeof(MODSYM));
		  gMODSYM(vaModSym);
		  cMODSYM.vaFirstProp = vaProp;
		  cMODSYM.vaNextModSym = 0;
		  pMODSYM(vaModSym);

		  if (!cMOD.vaFirstModSym)
		     cMOD.vaFirstModSym = cMOD.vaLastModSym = vaModSym;
		  else {
		     gMODSYM(cMOD.vaLastModSym).vaNextModSym = vaModSym;
		     pMODSYM(cMOD.vaLastModSym);
		     cMOD.vaLastModSym = vaModSym;
		  }
		  pMOD(vaMod);
		  break;
	       }
	       vaMod = cMOD.vaNextMod;
	    }
	    break2: ;  // duplicate Modsyms will cause goto here
	 ENM_END

	 vaProp = cPROP.vaNextProp;
      }
   }

   SetVMClient(VM_MISC);
}

VOID
CleanUp()
//	1. Remove symbols that have no references.
//	2. Remove symbols that have only references
//	3. Connect used symbols with no definition to <Unknown>
//
{
    WORD i;
    VA vaSym, vaProp, vaPropNext, vaPropPrev = vaNil;
    DEF def;
    BOOL fDelete;

    #define FExternAttr(attr) (!!(attr & SBR_ATR_DECL_ONLY))
    #define FFunctionAttr(attr) ((attr & SBR_TYPMASK) == SBR_TYP_FUNCTION)

    def.vaFileSym = vaUnknownSym;
    def.deflin    = 0;
    def.isbr      = 0xffff;

    SetVMClient(VM_CLEAN_REFS);

    for (i=0; i < cSymbolsMac; i++) {
	vaSym = rgvaSymSorted[i];

	vaPropPrev = vaNil;

	vaProp = gSYM(vaSym).vaFirstProp;

	while (vaProp) {
	    vaPropNext = gPROP(vaProp).vaNextProp;
	    fDelete = FALSE;

	    // figure out what to delete here

	    // if the symbol is used by anyone or uses anyone we must keep it
	    // regardless of all other considerations
	    //
	    if (((!cPROP.vaCalList) && (!cPROP.vaCbyList))  && (
		// at this point we know there are only refs & defs

		   // if it is totally unreferenced & undefined it can go
		   (cPROP.cref == 0 && (!cPROP.vaDefList))
		 ||
		   // if we're allowed to remove "useless" symbols then we try
	    	   ((!OptIu) && 
			// if there are only prototypes we can delete it
			(((!cPROP.vaDefList) && FExternAttr(cPROP.sattr))
		      ||
			// or if it is unreferenced and is not a function 
			(cPROP.cref == 0 && (!FFunctionAttr(cPROP.sattr))))))) {
				fDelete = TRUE;	// nuke it
	    }
	    else if (!cPROP.vaDefList) {

		// if we couldn't get rid of the thing, and there are no
		// definitions for it then we must make a fake definition
		// in the <Unknown> file.  This will happen (in particular)
		// for library functions that are called by someone
		//
		// library functions that are not called would fall under
		// the case of a symbol with only prototypes above

		VaAddList(&cPROP.vaDefList, &def, sizeof(def), grpDef);
		pPROP(vaProp);

		#ifdef DEBUG
		if (OptD & 32)
		    printf ("PROP unknown: %s\n", GetAtomStr (vaSym));
		#endif
	    }

	    if (fDelete) {
		#ifdef DEBUG
		if (OptD & 32)
		    printf ("PROP deleted: %s\n", GetAtomStr (vaSym));
		#endif

		cSYM.cprop--;

		if (vaPropPrev == vaNil) {
		    cSYM.vaFirstProp = vaPropNext;
		}
		else {
	  	    gPROP(vaPropPrev);
		    cPROP.vaNextProp = vaPropNext;
		    pPROP(vaPropPrev);
		}

		pSYM(vaSym);
	    }
	    else
		vaPropPrev = vaProp;	// prev = current 

	    vaProp = vaPropNext;
	}

	if (!cSYM.cprop) {
	    #ifdef DEBUG
	    if (OptD & 32)
		printf ("SYM deleted: %s\n", GetAtomStr (vaSym));
	    #endif
	    rgvaSymSorted[i] = vaNil;
	}
    }

    SetVMClient(VM_MISC);
}

BOOL
FWildMatch(char *pchPat, char *pchText)
// return TRUE if pchText matchs pchPat in the dos wildcard sense
//
// REVIEW FWildMatch for 1.2 file name support
//
{
    char chText, chPat;

    for (;;) {
	switch (*pchPat) {

	case '\0':
	    return *pchText == '\0';

	case '/':
	case '\\':
	    if (*pchText != '/' && *pchText != '\\')
		return FALSE;

	    pchText++;
	    pchPat++;
	    break;
	
	case '.':
	    pchPat++;
	    switch (*pchText) {

	    case '.':
		pchText++;
		break;

	    case '\0': case '/': case '\\':
		break;

	    default:
		return FALSE;
	    }
	    break;

	case '*':
	    pchText += strcspn(pchText, ":./\\");
	    pchPat  += strcspn(pchPat,  ":./\\");
	    break;

	case '?':
	    pchPat++;
	    switch (*pchText) {

	    case '\0': case '.': case '/': case '\\':
		break;

	    default:
		pchText++;
		break;
	    }
		
	    break;

	default:
	    chText = *pchText;
	    chPat  = *pchPat;

	    if (islower(chText)) chText = (char)toupper(chText);
	    if (islower(chPat))  chPat	= (char)toupper(chPat);

	    if (chText != chPat)
		return FALSE;
	   
	    pchPat++;
	    pchText++;
	    break;
	}
    }
}