#include <assert.h>
#include "symutil.h"
#include "private.h"
#include "dbgimage.h"
#include "cvinfo.h"
#include "exe_vxd.h"
#include "share.h"
#include "winbase.h"

// Stuff for Checking symbols

#define DBGSYM 2
#define PDBSYM 3
#define lengthof(a) (sizeof(a) / sizeof(a[0]))

BOOL CheckPrivate=FALSE;
BOOL CheckCodeView=TRUE;
PEXCLUDE_LIST pErrorFilterList=NULL;
P_LIST pCDIncludeList=NULL;
BOOL Recurse = FALSE;
BOOL LogCheckSumErrors = TRUE;

// private version of qsort used to avoid compat problems on NT4 and win2k.
// code is published from base\crts
extern
void __cdecl dbg_qsort(void *, size_t, size_t,
                       int (__cdecl *) (const void *, const void *));

#if defined(use_SymutilX)

static BOOL RSDSLibLoaded=FALSE;

// Typedefs
typedef BOOL ( __cdecl *PPDBOPENVALIDATE4 ) (
    const wchar_t *wszPDB,
    const char *szMode,
    PCSIG70 pcsig70,
    SIG sig,
    AGE age,
    OUT EC *pec,
    OUT wchar_t *wszError,
    size_t cchErrMax,
    OUT PDB **pppdb
    );

typedef BOOL ( __cdecl *PPDBCLOSE ) (
    PDB* ppdb
    );

typedef BOOL ( __cdecl *PDBIQUERYNEXTMOD ) (
    DBI* pdbi, Mod* pmod, Mod** ppmodNext
    );

typedef BOOL ( __cdecl *PMODCLOSE ) (
    Mod* pmod
    );

typedef BOOL ( __cdecl *PMODQUERYLINES ) (
    Mod* pmod, BYTE* pbLines, long* pcb
    );

typedef BOOL ( __cdecl *PMODQUERYSYMBOLS ) (
    Mod* pmod, BYTE* pbSym, long* pcb
    );

typedef BOOL ( __cdecl *PDBIQUERYTYPESERVER ) (
    DBI* pdbi, ITSM itsm, OUT TPI** pptpi
    );

typedef BOOL ( __cdecl *PPDBOPENTPI ) (
    PDB* ppdb, const char* szMode, OUT TPI** pptpi
    );

typedef BOOL ( __cdecl *PTYPESQUERYTIMINEX ) (
    TPI* ptpi
    );

typedef BOOL ( __cdecl *PTYPESQUERYTIMACEX ) (
    TPI* ptpi
    );

typedef BOOL ( __cdecl *PTYPESCLOSE ) (
    TPI* ptpi
    );

typedef BOOL ( __cdecl *PPDBOPENDBI ) (
    PDB* ppdb, const char* szMode, const char* szTarget, OUT DBI** ppdbi
    );

typedef BOOL ( __cdecl *PDBICLOSE ) (
    DBI* pdbi
    );

static PPDBOPENVALIDATE4 pPDBOpenValidate4 = NULL;
static PPDBCLOSE pPDBClose = NULL;
static PDBIQUERYNEXTMOD pDBIQueryNextMod = NULL;
static PMODCLOSE pModClose = NULL;
static PMODQUERYLINES pModQueryLines = NULL;
static PMODQUERYSYMBOLS pModQuerySymbols = NULL;
static PDBIQUERYTYPESERVER pDBIQueryTypeServer = NULL;
static PPDBOPENTPI pPDBOpenTpi = NULL;
static PTYPESQUERYTIMINEX pTypesQueryTiMinEx = NULL;
static PTYPESQUERYTIMACEX pTypesQueryTiMacEx = NULL;
static PTYPESCLOSE pTypesClose = NULL;
static PPDBOPENDBI pPDBOpenDBI = NULL;
static PDBICLOSE pDBIClose = NULL;

#endif

BOOL
ansi2wcs(
    PSTR  psz,
    PWSTR pwsz,
    DWORD pwszlen
);

typedef struct _FILE_INFO {
    DWORD       TimeDateStamp;
    DWORD       SizeOfImage;
    DWORD       CheckSum;
    TCHAR       szName[MAX_PATH];
} FILE_INFO, *PFILE_INFO;


PIMAGE_DOS_HEADER
MapFileHeader (
              LPTSTR szFileName,
              PHANDLE phFile,
              PSYM_ERR pSymErr
              );

PIMAGE_NT_HEADERS
GetNtHeader (
            PIMAGE_DOS_HEADER pDosHeader,
            HANDLE hDosFile,
            LPTSTR szFileName,
            PSYM_ERR pSymErr,
            LPBY_HANDLE_FILE_INFORMATION lpFileInfo  
            );

BOOL
ResourceOnlyDll(
               PVOID pImageBase,
               BOOLEAN bMappedAsImage
               );

PIMAGE_SEPARATE_DEBUG_HEADER
MapDbgHeader (
             LPTSTR szFileName,
             PHANDLE phFile,
             PSYM_ERR pSymErr
             );

BOOL
UnmapFile(
         LPCVOID phFileMap,
         HANDLE hFile
         );

IMAGE_DEBUG_DIRECTORY UNALIGNED *
GetDebugDirectoryInExe(
                      PIMAGE_DOS_HEADER pDosHeader,
                      ULONG *NumberOfDebugDirectories
                      );

IMAGE_DEBUG_DIRECTORY UNALIGNED *
GetDebugDirectoryInDbg(
                      PIMAGE_SEPARATE_DEBUG_HEADER pDbgHeader,
                      ULONG *NumberOfDebugDirectories
                      );

PIMAGE_SEPARATE_DEBUG_HEADER
MapMatchingDbgFile(
                  LPTSTR szSearchPath,
                  PFILE_INFO pFileInfo,
                  LPTSTR szFoundPath
                  );

BOOL
VerifyCV(
        LPTSTR szFileName,
        PCHAR pImageBase,
        IMAGE_DEBUG_DIRECTORY UNALIGNED *pDbgDir,
        LPTSTR szExtPath,
        LPTSTR szSearchPath,
        PSYM_ERR pSymErr,
        LPTSTR szRSDSDllToLoad
        );

LPSTR
GetMiscFile(
           PCHAR pImageBase,
           IMAGE_DEBUG_DIRECTORY UNALIGNED *pDbgDir
           );

BOOL
SearchForSymbolFile (
                    LPTSTR szSearchPath,
                    LPTSTR szSymName,
                    LPTSTR szPathExt,
                    PSYM_ERR pSymErr,
                    DWORD SymType,
                    PVOID  pelem1,
                    LPTSTR  szResult,
                    DWORD  dwResultLength,
                    LPTSTR szRSDSDllToLoad
                    );

USHORT
__cdecl
CompDbg(
       PVOID pelem1,
       LPTSTR szSymName,
       PSYM_ERR pSymErr
       );

USHORT
__cdecl
CompPdb(
       PVOID pelem1,
       LPTSTR szSymName,
       PSYM_ERR pSymErr,
       LPTSTR szRSDSDllToLoad
       );

BOOL
AddToSymbolsCDLog(
                 FILE *hSymCDLog,
                 PSYM_ERR pSymErr,
                 LPTSTR szSymbolPath,
                 LPTSTR szFileExt
                 );

BOOL PDBPrivateStripped(
                       PDB *ppdb,
                       DBI *pdbi
                       );

#if defined(use_SymutilX)
BOOL PDBPrivateStrippedX(
                       PDB *ppdb,
                       DBI *pdbi
                       );
#endif

BOOL
RemoveDuplicateSlashes(
   LPTSTR Str
); 

int __cdecl
SymComp(
       const void *e1,
       const void *e2
       );

PEXCLUDE_LIST
GetExcludeList(
              LPTSTR szFileName
              )

{
    PEXCLUDE_LIST pExcList;

    FILE  *fFile;
    TCHAR szCurFile[_MAX_FNAME+1], *c;
    TCHAR fname[_MAX_FNAME+1], ext[_MAX_EXT+1];
    DWORD i;
    LPTSTR szEndName;

    if (  (fFile = _tfopen(szFileName,_T("r") )) == NULL ) {
        // printf( "Cannot open the exclude file %s\n",szFileName );
        return FALSE;
    }

    pExcList = (PEXCLUDE_LIST)malloc(sizeof(EXCLUDE_LIST));

    if (pExcList)
    {
        pExcList->dNumFiles = 0;

        while ( _fgetts(szCurFile,_MAX_FNAME,fFile) ) {
            if ( szCurFile[0] == ';' ) continue;
            (pExcList->dNumFiles)++;
        }

        // Go back to the beginning of the file
        fseek(fFile,0,0);
        pExcList->szExcList = (LPTSTR*)malloc( sizeof(LPTSTR) *
                                               (pExcList->dNumFiles));
        if (pExcList->szExcList == NULL)
        {
            free(pExcList);
            pExcList = NULL;
        }
        else
        {
            i = 0;
            while ( i < pExcList->dNumFiles )
            {
                pExcList->szExcList[i] = NULL;

                memset(szCurFile,'\0',sizeof(TCHAR) * (_MAX_FNAME+1) );
                _fgetts(szCurFile,_MAX_FNAME,fFile);

                // Replace the \n with \0
                c = NULL;
                c  = _tcschr(szCurFile, '\n');
                if ( c != NULL)
                {
                    *c='\0';
                }

                if ( szCurFile[0] == ';' )
                {
                    continue;
                }

                if ( _tcslen(szCurFile) > _MAX_FNAME )
                {
                    printf("File %s has a string that is too large\n",szFileName);
                    continue;
                }

                // Allow for spaces and a ; after the file name
                // Move the '\0' back until it has erased the ';' and any
                // tabs and spaces that might come before it

                // Set the pointer to the ; if there is a comment
                szEndName = _tcschr(szCurFile, ';');

                // Set the pointer to the last character in the string if 
                // there wasn't a comment
                if (szEndName == NULL ) {
                    if (_tcslen(szCurFile) > 0 ) {
                        szEndName = szCurFile + _tcslen(szCurFile) - 1;
                    }
                }

                if (szEndName != NULL ) {
                    while ( *szEndName == ';' || *szEndName == ' '
                            || *szEndName == '\t' ) {
                        *szEndName = '\0';
                        if ( szEndName > szCurFile ) szEndName--;
                    }
                }

                pExcList->szExcList[i]=(LPTSTR)
                                       malloc( sizeof(TCHAR) * (_tcslen(szCurFile)+1) );

                if (pExcList->szExcList[i] == NULL )
                {
                    printf("Malloc failed for %s\n",szCurFile);
                }
                else
                {
                    _tsplitpath(szCurFile,NULL,NULL,fname,ext);

                    _tcscpy(pExcList->szExcList[i],fname);
                    _tcscat(pExcList->szExcList[i],ext);
                }

                i++;
            }

            // Sort the List
            dbg_qsort( (void*)pExcList->szExcList, (size_t)pExcList->dNumFiles,
                   (size_t)sizeof(LPTSTR), SymComp );
        }
    }

    fclose(fFile);

    return (pExcList);

}

BOOL
InExcludeList(
             LPTSTR szFileName,
             PEXCLUDE_LIST pExcludeList
             )

{
    DWORD i;
    int High;
    int Low;
    int Middle;
    int Result;

    // Lookup the name using a binary search
    if ( pExcludeList == NULL ) return FALSE;
    if ( pExcludeList->dNumFiles == 0 ) return FALSE;

    Low = 0;
    High = pExcludeList->dNumFiles - 1;
    while ( High >= Low ) {

        Middle = (Low + High) >> 1;
        Result = _tcsicmp( szFileName, pExcludeList->szExcList[Middle] );

        if ( Result < 0 ) {
            High = Middle - 1;

        } else if ( Result > 0 ) {
            Low = Middle + 1;

        } else {
            break;
        }
    }

    if ( High < Low ) return FALSE;

    return TRUE;
}


BOOL
InList(
      LPTSTR szFileName,
      P_LIST pExcludeList
      )

{
    DWORD i;
    int High;
    int Low;
    int Middle;
    int Result;

    // Lookup the name using a binary search
    if ( pExcludeList == NULL ) return FALSE;
    if ( pExcludeList->dNumFiles == 0 ) return FALSE;

    Low = 0;
    High = pExcludeList->dNumFiles - 1;
    while ( High >= Low ) {

        Middle = (Low + High) >> 1;
        Result = _tcsicmp( szFileName, pExcludeList->List[Middle].Path );

        if ( Result < 0 ) {
            High = Middle - 1;

        } else if ( Result > 0 ) {
            Low = Middle + 1;

        } else {
            break;
        }
    }

    if ( High < Low ) return FALSE;

    return TRUE;
}


int __cdecl
SymComp(
       const void *e1,
       const void *e2
       )
{
    LPTSTR* p1;
    LPTSTR* p2;

    p1 = (LPTSTR*)e1;
    p2 = (LPTSTR*)e2;

    return ( _tcsicmp(*p1,*p2) );
}


BOOL
CheckSymbols (
             LPTSTR ErrMsg,
             LPTSTR szSearchPath,
             LPTSTR szFileName,
             FILE   *hSymCDLog,
             ULONG SymchkFlag,
             BOOL Verbose,
             LPTSTR szRSDSDllToLoad
             )
/*++

Routine Description:

    This function accepts a file name and a symbol search path
    (delimited by ;) and determines if its symbols match.

Arguments:
    szSearchPath    Search path delimited by ;

    szFileName      Full path and name of the file to verify symbols for

    szCopyDest      If this is not a NULL value, then generate actually copy the
                    symbols to this destination

    SymchkFlag      Specifies action to take according to whether symbols are
                    split or not split.

                    Possible values:
                        ERROR_IF_SPLIT      Print an error if the image is split already
                        ERROR_IF_NOT_SPLIT  Error if image is not split
                        IGNORE_IF_SPLIT     Don't check symbols for split images

    Verbose         If true, print an output line for every file
                    If false, print output only for files that fail

Return Value:
    TRUE    if symbols are correct, or symbol checking was ignored
    FALSE   if symbols are not correct

--*/

{

    PIMAGE_NT_HEADERS pNtHeader = NULL;
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_SEPARATE_DEBUG_HEADER pDbgHeader= NULL;

    IMAGE_DEBUG_DIRECTORY UNALIGNED *DebugDirectory;
    IMAGE_DEBUG_DIRECTORY UNALIGNED *pDbgDir=NULL;
    PIMAGE_DEBUG_MISC pDebugMisc;

    PTCHAR pImageBase;

    ULONG NumberOfDebugDirectories;
    FILE_INFO FileInfo;
    LPSTR szMiscFileName;
    UINT i;
    PNB10I pDebugCV;


    TCHAR path_buffer[_MAX_PATH];
    TCHAR drive[_MAX_DRIVE];
    TCHAR dir[_MAX_DIR];
    TCHAR fname[_MAX_FNAME];
    TCHAR ext[_MAX_EXT];
    TCHAR szDbgFileName[_MAX_PATH*sizeof(TCHAR)];
    DWORD dwDbgFileNameLength = _MAX_PATH;

    HANDLE DbgFile = 0;
    HANDLE DosFile = 0;

    BOOL SymbolsOK = TRUE;
    BOOL CVFound = FALSE;
    BOOL MiscFound = FALSE;
    BOOL RawDataFound = FALSE;
    BOOL rc= FALSE;
    BY_HANDLE_FILE_INFORMATION HandleFileInfo;  

    SYM_ERR SymErr;

    // Get the file name and the extension
    _tsplitpath( szFileName,drive,dir,fname,ext);


    // Initialize SymErr
    memset( &SymErr,0,sizeof(SYM_ERR) );
    SymErr.Verbose = Verbose;
    _tcscpy( SymErr.szFileName, szFileName );
    _tcscpy( SymErr.szSymbolSearchPath, szSearchPath );
    _tcscpy( SymErr.szSymbolFileName, _T("") );
    _tcscpy( SymErr.szPdbErr, _T("") );
    _tcscpy( SymErr.szPdbFileName, _T("") );

    pDosHeader = MapFileHeader( szFileName, &DosFile, &SymErr );
    if ( SymErr.ErrNo > 0 ) {
        LogError( ErrMsg, &SymErr, 0);
        return TRUE;
    }

    pNtHeader = GetNtHeader( pDosHeader, DosFile, szFileName, &SymErr, &HandleFileInfo );
    if ( SymErr.ErrNo > 0 ) {
        UnmapFile((LPCVOID)pDosHeader,DosFile);
        LogError( ErrMsg, &SymErr,0 );
        return TRUE;
    }

    __try {
        // Resource Dll's shouldn't have symbols
        if ( ResourceOnlyDll((PVOID)pDosHeader, FALSE) ) {
            LogError( ErrMsg, &SymErr,RESOURCE_ONLY_DLL);
            SymbolsOK = TRUE;
            __leave;
        }

        // Get info out of file header for comparison later
        if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
            FileInfo.CheckSum = ((PIMAGE_NT_HEADERS32)pNtHeader)->OptionalHeader.CheckSum;
            FileInfo.SizeOfImage = ((PIMAGE_NT_HEADERS32)pNtHeader)->OptionalHeader.SizeOfImage;
        } else {
            if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
                FileInfo.CheckSum = ((PIMAGE_NT_HEADERS64)pNtHeader)->OptionalHeader.CheckSum;
                FileInfo.SizeOfImage = ((PIMAGE_NT_HEADERS64)pNtHeader)->OptionalHeader.SizeOfImage;
            } else {
                FileInfo.CheckSum = -1;
            }
        }
        FileInfo.TimeDateStamp = pNtHeader->FileHeader.TimeDateStamp;
        _tcscpy(FileInfo.szName,szFileName);


        // Locate the Debug Directory in this file
        DebugDirectory = NULL;
        DebugDirectory = GetDebugDirectoryInExe(
                                               pDosHeader,
                                               &NumberOfDebugDirectories
                                               );
        if (!DebugDirectory) {
            LogError( ErrMsg, &SymErr,NO_DEBUG_DIRECTORIES);
            SymbolsOK = FALSE;
            __leave;
        }
        pImageBase = (PCHAR) pDosHeader;

        // Do some checks on the Debug Directories

        // Debug Information is stripped, we need the misc directory to find
        // out the DBG file name
        if ( (pNtHeader->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) ) {
            if (SymchkFlag & ERROR_IF_SPLIT) {
                // Image is split, log error and continue;
                LogError( ErrMsg, &SymErr,ERROR_IF_SPLIT);
                SymbolsOK = FALSE;
                __leave;
            } else if (SymchkFlag & IGNORE_IF_SPLIT ) {
                // Image is split, don't check the symbols
                LogError( ErrMsg, &SymErr,IGNORE_IF_SPLIT);
                SymbolsOK = TRUE;
                __leave;
            }

            // Get the MISC entry
            i=0;
            while ( (i<NumberOfDebugDirectories) &&
                    ((DebugDirectory+i)->Type != IMAGE_DEBUG_TYPE_MISC) ) i++;

            if (i >= NumberOfDebugDirectories) {
                LogError( ErrMsg, &SymErr,NO_MISC_ENTRY);
                SymbolsOK = FALSE;
                __leave;
            }

            szMiscFileName = NULL;
            szMiscFileName = GetMiscFile(pImageBase,DebugDirectory + i);
            if (!szMiscFileName) {
                LogError( ErrMsg, &SymErr,NO_FILE_IN_MISC);
                SymbolsOK = FALSE;
                __leave;
            }

            // Debug info is stripped.
            // First, locate the correct DBG file
            rc = SearchForSymbolFile (
                                       szSearchPath,
                                       szMiscFileName,
                                       ext+1, // Extension of the image
                                       &SymErr,
                                       DBGSYM,
                                       (PVOID)&FileInfo,
                                       szDbgFileName,
                                       dwDbgFileNameLength,
                                       szRSDSDllToLoad );

            if (!rc) {
                LogDbgError(ErrMsg, &SymErr);
                SymbolsOK = FALSE;
                __leave;
            } else if ( hSymCDLog != NULL) {
                AddToSymbolsCDLog( hSymCDLog, &SymErr, SymErr.szSymbolFileName, ext+1);
            }

            pDbgHeader = NULL;
            pDbgHeader = MapDbgHeader(szDbgFileName,&DbgFile, &SymErr);
            if (!pDbgHeader) {
                SymbolsOK = FALSE;
                // 0 means info is inside of SymErr
                LogError(ErrMsg, &SymErr,0);
                __leave;
            }

            DebugDirectory = NULL;
            DebugDirectory = GetDebugDirectoryInDbg(
                                                   pDbgHeader,
                                                   &NumberOfDebugDirectories
                                                   );
            if (!DebugDirectory) {
                LogError( ErrMsg, &SymErr,NO_DEBUG_DIRECTORIES_IN_DBG_HEADER);
                SymbolsOK = FALSE;
                __leave;
            }
            pImageBase = (PCHAR)pDbgHeader;
        }

        // Image is not split
        else {

            // VC 6 compiler always produces non-split images.
            // Check the debug directories to determine if this is a VC 6 image.

            RawDataFound = FALSE;
            CVFound = FALSE;
            MiscFound = FALSE;
            for ( i=0; i< NumberOfDebugDirectories; i++ ) {
                pDbgDir = DebugDirectory + i;
                switch (pDbgDir->Type) {
                    case IMAGE_DEBUG_TYPE_MISC:
                        MiscFound = TRUE;
                        break;

                    case IMAGE_DEBUG_TYPE_CODEVIEW:
                        CVFound = TRUE;
                        break;

                    default:
                        // Nothing except the CV entry should point to raw data
                        if ( pDbgDir->SizeOfData != 0 ) {
                            RawDataFound = TRUE;
                        }
                        break;
                }
            }

            if ( !MiscFound && CVFound ) {

                // This is using the 6.0 linker.  It does not
                // need to be split into iamge + dbg, so don't
                // give an error for it, unless there is another
                // debug directory with a non-null pointer to raw data.

                if ( RawDataFound ) {
                    LogError( ErrMsg, &SymErr, EXTRA_RAW_DATA_IN_6);
                    return ( FALSE);
                } else {
                    // Continue - image should not be split
                }
            } else {
                DWORD CertificateSize;
                PVOID pCertificates = ImageDirectoryEntryToData(
                                                     (PVOID)pDosHeader,
                                                     FALSE,
                                                     IMAGE_DIRECTORY_ENTRY_SECURITY,
                                                     &CertificateSize
                                                     );
                if ( pCertificates ) {
                    // Image is signed and non-split
                    // It should be split before it is signed
                    LogError( ErrMsg, &SymErr, SIGNED_AND_NON_SPLIT);
                    SymbolsOK = FALSE;
                    __leave;
                } else if (SymchkFlag & ERROR_IF_NOT_SPLIT) {
                    // Image isn't split, log error and continue;
                    LogError( ErrMsg, &SymErr,ERROR_IF_NOT_SPLIT);
                    SymbolsOK = FALSE;
                    __leave;
                }
            }

            // File with the DBG info is the original image file name
            // This line may not be necessary.
            _tcscpy( SymErr.szSymbolFileName, szFileName );
        }

        CVFound = FALSE;
        // Process the Debug Directories
        for ( i=0; i<NumberOfDebugDirectories; i++) {
            pDbgDir = DebugDirectory + i;
            switch (pDbgDir->Type) {

                // Don't need to process entry since directory table has
                // already been located.
                case IMAGE_DEBUG_TYPE_MISC:  break;

                case IMAGE_DEBUG_TYPE_CODEVIEW:
                    CVFound = TRUE;
                    if ( !VerifyCV(
                                  szFileName,
                                  pImageBase,
                                  pDbgDir,
                                  ext+1,
                                  szSearchPath,
                                  &SymErr,
                                  szRSDSDllToLoad
                                  )
                       ) {
                        LogPdbError(ErrMsg, &SymErr);
                        SymbolsOK = FALSE;
                    } else if (hSymCDLog != NULL && _tcscmp(SymErr.szPdbFileName, "") ) {
                        // Copy SymErr.szPdbFileName to Symbol Dest\ext
                        AddToSymbolsCDLog( hSymCDLog, &SymErr, SymErr.szPdbFileName, ext+1 );
                    }
                    break;
                default:    break;
            }
        }
        if (!CVFound && CheckCodeView ) {
            SymErr.ErrNo = NO_CODE_VIEW;
            LogPdbError(ErrMsg, &SymErr);
            SymbolsOK=FALSE;
        }
    } __finally {
        UnmapFile((LPCVOID)pDbgHeader,DbgFile);
        UnmapFile((LPCVOID)pDosHeader,DosFile);
    }

    if (SymbolsOK) {
        LogError( ErrMsg, &SymErr, IMAGE_PASSED );
    }
    return SymbolsOK;
}


PIMAGE_DOS_HEADER
MapFileHeader (
              LPTSTR szFileName,
              PHANDLE phFile,
              PSYM_ERR pSymErr
              )

{

    /*
        Creates a file mapping and returns Handle for the DOS_HEADER
        If the file does not have a DOS_HEADER, then it returns NULL.


    */
    HANDLE hFileMap;
    PIMAGE_DOS_HEADER pDosHeader;
    DWORD  dFileType;
    BOOL rc;


    // phFile map needs to be returned, so it can be closed later
    (*phFile) = CreateFile( (LPCTSTR) szFileName,
                            GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL
                          );

    if (*phFile == INVALID_HANDLE_VALUE) {
        pSymErr->ErrNo  = CREATE_FILE_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        return(NULL);
    }

    hFileMap = CreateFileMapping( *phFile,
                                  NULL,
                                  PAGE_READONLY,
                                  0,
                                  0,
                                  NULL
                                );

    if ( hFileMap == INVALID_HANDLE_VALUE) {
        pSymErr->ErrNo = CREATE_FILE_MAPPING_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        CloseHandle(*phFile);
        return(NULL);
    }

    pDosHeader = (PIMAGE_DOS_HEADER) MapViewOfFile( hFileMap,
                                                    FILE_MAP_READ,
                                                    0,  // high
                                                    0,  // low
                                                    0   // whole file
                                                  );

    rc = CloseHandle(hFileMap);

    if ( !pDosHeader ) {
        pSymErr->ErrNo = MAPVIEWOFFILE_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        CloseHandle(*phFile);
        return(NULL);
    }

    //
    // Check to determine if this is an NT image (PE format)

    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        pSymErr->ErrNo = NO_DOS_HEADER;
        UnmapViewOfFile(pDosHeader);
        CloseHandle(*phFile);
        return(NULL);
    }
    return (pDosHeader);
}


PIMAGE_NT_HEADERS
GetNtHeader ( PIMAGE_DOS_HEADER pDosHeader,
              HANDLE hDosFile,
              LPTSTR szFileName,
              PSYM_ERR pSymErr,
              LPBY_HANDLE_FILE_INFORMATION lpFileInfo  
            )
{

    /*
        Returns the pointer the address of the NT Header.  If there isn't
        an NT header, it returns NULL
    */
    PIMAGE_NT_HEADERS pNtHeader;

    if (!GetFileInformationByHandle( hDosFile, lpFileInfo)) {
        pSymErr->ErrNo = GET_FILE_INFO_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        return(NULL);
    }

    if ( ((ULONG)(pDosHeader->e_lfanew) & 3) != 0) {

        //
        // The image header is not aligned on a long boundary.
        // Report this as an invalid protect mode image.
        //

        pSymErr->ErrNo = HEADER_NOT_ON_LONG_BOUNDARY;
        return (NULL);
    }


    if ((ULONG)(pDosHeader->e_lfanew) > lpFileInfo->nFileSizeLow) {
        pSymErr->ErrNo = IMAGE_BIGGER_THAN_FILE;
        return (NULL);
    }


    pNtHeader = (PIMAGE_NT_HEADERS)((PCHAR)pDosHeader +
                                    (ULONG)pDosHeader->e_lfanew);

    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
        pSymErr->ErrNo = NOT_NT_IMAGE;
        return(NULL);
    }

    return (pNtHeader);
}


PIMAGE_SEPARATE_DEBUG_HEADER
MapDbgHeader (
             LPTSTR szFileName,
             PHANDLE phFile,
             PSYM_ERR pSymErr
             )
{
    HANDLE hFileMap;
    PIMAGE_SEPARATE_DEBUG_HEADER pDbgHeader;
    DWORD  dFileType;


    (*phFile) = CreateFile( (LPCTSTR) szFileName,
                            GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL
                          );

    if (*phFile == INVALID_HANDLE_VALUE) {
        pSymErr->ErrNo  = CREATE_FILE_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        CloseHandle(*phFile);
        return(NULL);
    }

    hFileMap = CreateFileMapping( *phFile,
                                  NULL,
                                  PAGE_READONLY,
                                  0,
                                  0,
                                  NULL
                                );

    if ( hFileMap == INVALID_HANDLE_VALUE) {
        pSymErr->ErrNo = CREATE_FILE_MAPPING_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        CloseHandle(*phFile);
        return(NULL);
    }


    pDbgHeader = (PIMAGE_SEPARATE_DEBUG_HEADER) MapViewOfFile( hFileMap,
                                                               FILE_MAP_READ,
                                                               0,  // high
                                                               0,  // low
                                                               0   // whole file
                                                             );
    CloseHandle(hFileMap);

    if ( !pDbgHeader ) {
        pSymErr->ErrNo = MAPVIEWOFFILE_FAILED;
        pSymErr->ErrNo2 = GetLastError();
        _tcscpy( pSymErr->szFileName, szFileName);
        UnmapFile((LPCVOID)pDbgHeader, *phFile);
        return(NULL);
    }

    return (pDbgHeader);
}

BOOL
UnmapFile( LPCVOID phFileMap, HANDLE hFile )
{
BOOL rc;

    if ((PHANDLE)phFileMap != NULL) {
        FlushViewOfFile(phFileMap,0);
        rc = UnmapViewOfFile( phFileMap );
        if (rc == 0) {
            printf("UnmapView of File failed with GetLastError=%d\n",GetLastError() );
        }
    }
    if (hFile) {
        rc = CloseHandle(hFile);
    }
    return TRUE;
}


IMAGE_DEBUG_DIRECTORY UNALIGNED *
GetDebugDirectoryInExe(
                      PIMAGE_DOS_HEADER pDosHeader,
                      ULONG *NumberOfDebugDirectories
                      )
{

    /* Exe is already mapped and a pointer to the base is
       passed in. Find a pointer to the Debug Directories
    */
    ULONG size;

    IMAGE_DEBUG_DIRECTORY UNALIGNED *pDebugDirectory = NULL;
    ULONG DebugDirectorySize;
    PIMAGE_SECTION_HEADER pSectionHeader;

    size = sizeof(IMAGE_DEBUG_DIRECTORY);

    pDebugDirectory = (PIMAGE_DEBUG_DIRECTORY)
                      ImageDirectoryEntryToDataEx (
                                                  (PVOID)pDosHeader,
                                                  FALSE,
                                                  IMAGE_DIRECTORY_ENTRY_DEBUG,
                                                  &DebugDirectorySize,
                                                  &pSectionHeader );

    if (pDebugDirectory) {
        (*NumberOfDebugDirectories) =
        DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
        return (pDebugDirectory);
    } else return(NULL);
}

IMAGE_DEBUG_DIRECTORY UNALIGNED *
GetDebugDirectoryInDbg(
                      PIMAGE_SEPARATE_DEBUG_HEADER pDbgHeader,
                      ULONG *NumberOfDebugDirectories
                      )
/*  Dbg is already mapped and a pointer to the base is
    passed in.  Returns a pointer to the Debug directories
*/
{
    IMAGE_DEBUG_DIRECTORY UNALIGNED *pDebugDirectory = NULL;

    pDebugDirectory = (PIMAGE_DEBUG_DIRECTORY) ((PCHAR)pDbgHeader +
                                                sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
                                                pDbgHeader->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
                                                pDbgHeader->ExportedNamesSize);

    if (!pDebugDirectory) {
        return(NULL);
    }

    (*NumberOfDebugDirectories) =   pDbgHeader->DebugDirectorySize /
                                    sizeof(IMAGE_DEBUG_DIRECTORY);
    return (pDebugDirectory);

}


BOOL
VerifyCV(
        LPTSTR szFileName,
        PCHAR pImageBase,
        IMAGE_DEBUG_DIRECTORY UNALIGNED *pDbgDir,
        LPTSTR szExtPath,
        LPTSTR szSearchPath,
        PSYM_ERR pSymErr,
        LPTSTR szRSDSDllToLoad
        )

{
    TCHAR szFName[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];
    TCHAR szPdbFileName[_MAX_PATH*sizeof(TCHAR)];
    DWORD dwPdbFileNameLength = _MAX_PATH;
    BOOL rc;


    PCVDD pDebugCV = (PCVDD) (pImageBase +
                              pDbgDir->PointerToRawData );

    // Initialize pSymErr
    pSymErr->PdbFileFound = FALSE;
    pSymErr->PdbValid = FALSE;
    _tcscpy(pSymErr->szPdbErr,_T("") );
    _tcscpy(pSymErr->szPdbFileName,_T("") );

    switch (pDebugCV->dwSig)
    {

    case '05BN': 
        // CVpack failed
        // Symbols are not OK
        _tcscpy(pSymErr->szPdbErr,_T("Codeview type is NB05") );
        return (FALSE);
        break;
    case '90BN': 
        // For NB09 and NB11, the codeview info is in the image file
        // Thus, no need to locate symbols.  They are probably OK.
        return(TRUE);
        break;
    case '11BN':
        return(TRUE);
        break;
    case '01BN':
        // Symbols are type NB10
        // Locate the PDB information

        rc = SearchForSymbolFile (
                                            szSearchPath,
                                            pDebugCV->nb10i.szPdb, // File Name
                                            szExtPath,
                                            pSymErr,
                                            PDBSYM,
                                            (PVOID)pDebugCV,
                                            szPdbFileName,
                                            dwPdbFileNameLength,
                                            szRSDSDllToLoad
                                            );
        if (rc ) {
            // SearchForSymbolFile already gave pSymErr->szPdbFilename its value
            return TRUE;
        } else {
            _tsplitpath(pDebugCV->nb10i.szPdb,NULL,NULL,szFName,szExt);
            _tcscpy(pSymErr->szPdbFileName,szFName);
            _tcscat(pSymErr->szPdbFileName,szExt);
            return FALSE;
        }
        break;
    case 'SDSR':
        rc = SearchForSymbolFile (
                                            szSearchPath,
                                            pDebugCV->rsdsi.szPdb, // File Name
                                            szExtPath,
                                            pSymErr,
                                            PDBSYM,
                                            (PVOID)pDebugCV,
                                            szPdbFileName,
                                            dwPdbFileNameLength,
                                            szRSDSDllToLoad
                                            );
        if (rc ) {
            // SearchForSymbolFile already gave pSymErr->szPdbFilename its value
            return TRUE;
        } else {
            _tsplitpath(pDebugCV->rsdsi.szPdb,NULL,NULL,szFName,szExt);
            _tcscpy(pSymErr->szPdbFileName,szFName);
            _tcscat(pSymErr->szPdbFileName,szExt);
            return FALSE;
        }
        break;
    default:
        _tcscpy(pSymErr->szPdbErr,
                _T("Codeview info is not NB09, NB10, or NB11\n") );
        return(FALSE);
        break;
    }
}


LPSTR
GetMiscFile(
           PCHAR pImageBase,
           IMAGE_DEBUG_DIRECTORY UNALIGNED *pDbgDir
           )

{
    PIMAGE_DEBUG_MISC pDebugMisc;
    LPSTR szData;

    // Nothing to verify, but here is how to get to the DBG entry
    // Get the debug directory

    pDebugMisc = (PIMAGE_DEBUG_MISC)(pImageBase +
                                     pDbgDir->PointerToRawData );
    szData = (LPSTR) &pDebugMisc->Data[0];
    return(szData);
}


BOOL SearchForSymbolFile (   LPTSTR     szSearchPath, // ; separated search path
                             LPTSTR     szSymName,    // Symbol File to search for
                             LPTSTR     szPathExt,    // Extension directory
                             PSYM_ERR   pSymErr,
                             DWORD      dSymType,
                             PVOID      pelem1,
                             LPTSTR     szResult,
                             DWORD      dwResultLength,
                             LPTSTR     szRSDSDllToLoad
                           )

{
    /*++

    Routine Description:
       For each symbol path in szSearchPath, this function looks in the path
       and in the path concatenated with the szPathExt subdirectory.

    Arguments:
        szSearchPath    ; separated symbol path

        szSymName       Symbol file to search for

        szPathExt       Extension for the image that the symbol file matches

        pSymErr         Error structure

        dSymType        Type of symbol.  Possible values: DBGSYM, PDBSYM

        pelem1          Pointer to structure needed for comparing the symbols

        szResult        OUT Filename returned
        dwResultLength  IN Length of szResult


    Return Value:
        If the symbol file is found and verifies, it returns the full path and
        name of the symbol file.  Otherwise, it returns NULL.

    --*/

    TCHAR szTmpSearchPath[_MAX_PATH];
    TCHAR szExtPath[_MAX_PATH];
    TCHAR szFName[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];

    LPTSTR szResultFileName;
    LPTSTR seps = _T(";");
    LPTSTR szCurPath;
    DWORD  FoundLength;
    DWORD  CurLen, ExtLen;

    _tcscpy( szResult,_T("") );

    // Make a copy of szSearchPath because tcstok alters it
    _tcscpy(szTmpSearchPath,(LPCTSTR)szSearchPath);

    // Strip FileName from its path
    _tsplitpath(szSymName, NULL, NULL, szFName, szExt);
    _tcscat(szFName, szExt);

    // Try each search path
    szCurPath = _tcstok(szTmpSearchPath,seps);

    while ( szCurPath != NULL ) {

        FoundLength = SearchPath( szCurPath,
                                  szFName,
                                  NULL,
                                  dwResultLength,
                                  szResult,
                                  &szResultFileName
                                );
        if (FoundLength > 0 && FoundLength < dwResultLength) {

            switch ( dSymType ) {
                case DBGSYM:
                    // Status info, a match was found
                    pSymErr->SymbolFileFound = TRUE;
                    _tcscpy(pSymErr->szSymbolFileName, szResult);

                    // Validate the DBG file
                    if ( CompDbg(pelem1, szResult, pSymErr) ) {
                        return (TRUE);
                    }
                    break;

                case PDBSYM:
                    // Status info, a match was found
                    pSymErr->PdbFileFound = TRUE;
                    _tcscpy(pSymErr->szPdbFileName, szResult);

                    // Validate the PDB file
                    if ( CompPdb(pelem1, szResult, pSymErr, szRSDSDllToLoad) ) {
                        return (TRUE);
                    }
                    break;

                default: break;
            }
        }

        // Now try the path with szPathExt at the end of it
        // Don't try it if current path already has the extension
        // at the end of it
        CurLen = _tcslen(szCurPath);
        ExtLen = _tcslen(szPathExt);

        if ( _tcscmp ( (szCurPath + CurLen - ExtLen), szPathExt ) ) {

            _tcscpy(szExtPath, szCurPath);
            _tcscat(szExtPath, _T("\\") );
            _tcscat(szExtPath, szPathExt);

            FoundLength = SearchPath( szExtPath,
                                      szFName,
                                      NULL,
                                      dwResultLength,
                                      szResult,
                                      &szResultFileName
                                    );
            if (FoundLength > 0 && FoundLength < dwResultLength) {
                switch ( dSymType ) {
                    case DBGSYM:
                        // Status info, a match was found
                        pSymErr->SymbolFileFound = TRUE;
                        _tcscpy(pSymErr->szSymbolFileName, szResult);

                        // Validate the DBG file
                        if ( CompDbg(pelem1, szResult, pSymErr) ) {
                            return (TRUE);
                        }
                        break;
                    case PDBSYM:
                        // Status info, a match was found
                        pSymErr->PdbFileFound = TRUE;
                        _tcscpy(pSymErr->szPdbFileName, szResult);

                        // Validate the PDB file
                        if ( CompPdb(pelem1, szResult, pSymErr, szRSDSDllToLoad) ) {
                            return (TRUE);
                        }
                        break;
                    default: break;
                }
            }
        }
        szCurPath = _tcstok(NULL,seps);
    }

    // Symbol wasn't found.  Put name of file into SymErr for error message
    switch ( dSymType ) {
        case DBGSYM:
            _tcscpy(pSymErr->szSymbolFileName,szFName);
            break;
        case PDBSYM:
            _tcscpy(pSymErr->szPdbFileName, szFName);
            break;
        default: break;
    }

    return (FALSE);
}


USHORT
__cdecl
CompDbg(
       PVOID pelem1,
       LPTSTR szSymName,
       PSYM_ERR pSymErr
       )

/*++

    Routine Description:
        Validates the DBG against the original image

    Return Value:
        TRUE -  DBG matches
        FALSE - DBG doesn't match timedatestamp and checksum


--*/
{

    PFILE_INFO pFileInfo;
    PIMAGE_SEPARATE_DEBUG_HEADER pDbgHeader;
    HANDLE hFile;

    pFileInfo = (PFILE_INFO)(pelem1);
    pDbgHeader = MapDbgHeader ( szSymName, &hFile, pSymErr );

    if (pDbgHeader == NULL) {
        printf("ERROR: CompDbg(), %s was not opened successfully\n",szSymName);
        UnmapFile((LPCVOID)pDbgHeader, hFile);
        return FALSE;
    }
    if (pDbgHeader->Signature != IMAGE_SEPARATE_DEBUG_SIGNATURE) {
        printf("ERROR: CompDbg(), %s doesn't have Debug signature in header\n", szSymName);
        UnmapFile((LPCVOID)pDbgHeader, hFile);
        return FALSE;
    }

    // Record specifics of whether timedatestamp and checksum matched
    if ( pDbgHeader->SizeOfImage == pFileInfo->SizeOfImage ) {
        pSymErr->SizeOfImageMatch = TRUE;
    } else pSymErr->SizeOfImageMatch = FALSE;

    if ( pDbgHeader->TimeDateStamp == pFileInfo->TimeDateStamp ) {
        pSymErr->TimeDateStampsMatch = TRUE;
    } else pSymErr->TimeDateStampsMatch = FALSE;

    if ( pDbgHeader->CheckSum == pFileInfo->CheckSum ) {
        pSymErr->CheckSumsMatch = TRUE;
    } else pSymErr->CheckSumsMatch = FALSE;

    // Just check the timedatestamp for determining if the DBG matches
    // VC and KD don't pay attention to the checksum anymore.
    if ( pSymErr->TimeDateStampsMatch  &&
         pSymErr->SizeOfImageMatch &&
         ( !LogCheckSumErrors || pSymErr->CheckSumsMatch ) ) {
        UnmapFile((LPCVOID)pDbgHeader, hFile);
        return TRUE;
    } else {
        UnmapFile((LPCVOID)pDbgHeader, hFile);
        return FALSE;
    }
}

USHORT
__cdecl
CompPdb(
       PVOID pelem1,
       LPTSTR szSymName,
       PSYM_ERR pSymErr,
       LPTSTR szRSDSDllToLoad
       )

/*++

    Routine Description:
        Validates the PDB

    Return Value:
        TRUE    PDB validates
        FALSE   PDB doesn't validate
--*/

{

    PCVDD pPdbInfo;
    WCHAR wszSymName[_MAX_PATH + 1];

    BOOL valid=FALSE;
    PDB *pdb;
    EC ec;
    char  szError[cbErrMax] = _T("");
    wchar_t wszError[cbErrMax] = L"";
    DBI *pdbi;
    HMODULE hDll;

    pdb=NULL;
    pPdbInfo = (PCVDD)(pelem1);

    switch (pPdbInfo->dwSig)
    {
    case '01BN':
        __try
        {
            valid = PDBOpenValidate(szSymName,
                            NULL,
                            _T("r"),
                            pPdbInfo->nb10i.sig,
                            pPdbInfo->nb10i.age,
                            &ec,
                            szError,
                            &pdb
                           );

           if ( !valid ) {
               pSymErr->PdbValid = FALSE;
               __leave;
           } else pSymErr->PdbValid = TRUE;


           if (!CheckPrivate) {
               PDBClose(pdb);
               __leave;
           }

           // Verify that private information is stripped
           // Verify that line information is removed

           if ( !PDBOpenDBI(pdb, pdbRead, NULL, &pdbi) ) {
               // OpenDBI failed
               pSymErr->PdbValid = FALSE;
               pSymErr->ErrNo = PDB_MAY_BE_CORRUPT;
               DBIClose(pdbi);
               __leave;
           }

           if ( !PDBPrivateStripped(pdb, pdbi) ) {
               //Error - Source line info is not stripped
               pSymErr->PdbValid = FALSE;
               pSymErr->ErrNo = PRIVATE_INFO_NOT_REMOVED;
           }

           DBIClose(pdbi);
           PDBClose(pdb);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            pSymErr->PdbValid = FALSE;
            valid=FALSE;
        }

        return (USHORT)pSymErr->PdbValid;
        break;

    case 'SDSR':

#if defined(use_SymutilX)
        if ( !RSDSLibLoaded ) {
            hDll = LoadLibrary( szRSDSDllToLoad );
            if (hDll != NULL) {
                RSDSLibLoaded = TRUE;
                pPDBOpenValidate4 = ( PPDBOPENVALIDATE4 ) GetProcAddress( hDll, "PDBOpenValidate4" );
                if (pPDBOpenValidate4 == NULL ) {
                    _tcscpy(pSymErr->szPdbErr,"Cannot load PDBOpenValidate4");
                    return (FALSE);
                }
                pPDBClose = ( PPDBCLOSE ) GetProcAddress( hDll, "PDBClose" );
                if (pPDBClose == NULL ) {
                    _tcscpy(pSymErr->szPdbErr,"Cannot load PDBClose");
                    return (FALSE);
                } 
            } else {
                if (_tcslen(szRSDSDllToLoad) + _tcslen("Cannot load ") + 1 < MAX_PDB_ERR ) {
                    _tcscpy(pSymErr->szPdbErr,"Cannot load ");
                    _tcscat(pSymErr->szPdbErr, szRSDSDllToLoad);
                } else {
                    pSymErr->ErrNo = CANNOT_LOAD_RSDS;
                }
                pSymErr->PdbValid = FALSE;
                return (FALSE);
            }
        }
#endif

        ansi2wcs(szSymName, wszSymName, lengthof(wszSymName)); 

        __try
        {
#if defined(use_SymutilX)
            valid = pPDBOpenValidate4(wszSymName,
#else
            valid = PDBOpenValidate4(wszSymName,
#endif
                            _T("r"),
                            (PCSIG70) &(pPdbInfo->rsdsi.guidSig),
                            0,
                            (AGE) pPdbInfo->rsdsi.age,
                            &ec,
                            wszError,
                            cbErrMax,
                            &pdb
                           );
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            valid = FALSE;
        }

        if ( !valid ) {
            pSymErr->PdbValid = FALSE;

            return FALSE;
        } else pSymErr->PdbValid = TRUE;


        if (!CheckPrivate) {
#if defined(use_SymutilX)
            pPDBClose(pdb);
#else
            PDBClose(pdb);
#endif
            return (USHORT)pSymErr->PdbValid;
        }

        // Verify that private information is stripped
        // Verify that line information is removed

#if defined(use_SymutilX)
        if ( pDBIQueryNextMod == NULL ) {
            pDBIQueryNextMod = ( PDBIQUERYNEXTMOD ) GetProcAddress( hDll, "DBIQueryNextMod" );
            if (pPDBOpenValidate4 == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load PDBClose, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pModClose == NULL ) {
            pModClose = ( PMODCLOSE ) GetProcAddress( hDll, "ModClose" );
            if (pModClose == NULL ) {  
                _tcscpy(pSymErr->szPdbErr,"Cannot load PDBClose, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pModQueryLines == NULL ) {
            pModQueryLines = ( PMODQUERYLINES ) GetProcAddress( hDll, "ModQueryLines" );
            if (pModQueryLines == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load ModQueryLines, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pModQuerySymbols == NULL ) {
            pModQuerySymbols = ( PMODQUERYSYMBOLS ) GetProcAddress( hDll, "ModQuerySymbols" );
            if (pModQuerySymbols == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load ModQuerySymbols, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pDBIQueryTypeServer == NULL ) {
            pDBIQueryTypeServer = ( PDBIQUERYTYPESERVER ) GetProcAddress( hDll, "DBIQueryTypeServer" );
            if (pDBIQueryTypeServer == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load DBIQueryTypeServer, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pPDBOpenTpi == NULL ) {
            pPDBOpenTpi = ( PPDBOPENTPI ) GetProcAddress( hDll, "PDBOpenTpi" );
            if (pPDBOpenTpi == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load PDBOpenTpi, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pTypesQueryTiMinEx == NULL ) {
            pTypesQueryTiMinEx = ( PTYPESQUERYTIMINEX ) GetProcAddress( hDll, "TypesQueryTiMinEx" );
            if (pTypesQueryTiMinEx == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load TypesQueryTiMinEx, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pTypesQueryTiMacEx == NULL ) {
            pTypesQueryTiMacEx = ( PTYPESQUERYTIMACEX ) GetProcAddress( hDll, "TypesQueryTiMacEx" );
            if (pTypesQueryTiMacEx == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load TypesQueryTiMacEx, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pTypesClose == NULL ) {
            pTypesClose = ( PTYPESCLOSE ) GetProcAddress( hDll, "TypesClose" );
            if (pTypesClose == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load TypesClose, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pPDBOpenDBI == NULL ) {
            pPDBOpenDBI = ( PPDBOPENDBI ) GetProcAddress( hDll, "PDBOpenDBI" );
            if (pPDBOpenDBI == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load PDBOpenDBI, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( pDBIClose == NULL ) {
            pDBIClose = ( PDBICLOSE ) GetProcAddress( hDll, "DBIClose" );
            if (pDBIClose == NULL ) {
                _tcscpy(pSymErr->szPdbErr,"Cannot load DBIClose, GetProcAddress failed.");
                return (FALSE);
            }
        }

        if ( !pPDBOpenDBI(pdb, pdbRead, NULL, &pdbi) ) {
            // OpenDBI failed
            pSymErr->ErrNo = PDB_MAY_BE_CORRUPT;
            pDBIClose(pdbi);
            return FALSE;
        }

        if ( !PDBPrivateStrippedX(pdb, pdbi) ) {
            //Error - Source line info is not stripped
            pSymErr->PdbValid = FALSE;
            pSymErr->ErrNo = PRIVATE_INFO_NOT_REMOVED;
        }

        pDBIClose(pdbi);
        pPDBClose(pdb);
        return (USHORT)pSymErr->PdbValid;
        break;
#else
        if ( !PDBOpenDBI(pdb, pdbRead, NULL, &pdbi) ) {
            // OpenDBI failed
            pSymErr->ErrNo = PDB_MAY_BE_CORRUPT;
            DBIClose(pdbi);
            return FALSE;
        }

        if ( !PDBPrivateStripped(pdb, pdbi) ) {
            //Error - Source line info is not stripped
            pSymErr->PdbValid = FALSE;
            pSymErr->ErrNo = PRIVATE_INFO_NOT_REMOVED;
        }

        DBIClose(pdbi);
        PDBClose(pdb);
        return (USHORT)pSymErr->PdbValid;
        break;

#endif

    default: break;
    }
    return (FALSE);
}


BOOL
ResourceOnlyDll(
               PVOID pImageBase,
               BOOLEAN bMappedAsImage
               )

/*++

Routine Description:

    Returns true if the image is a resource only dll.

--*/

{

    PVOID pExports, pImports, pResources;
    DWORD dwExportSize, dwImportSize, dwResourceSize;
    BOOL fResourceOnlyDll;

    pExports = ImageDirectoryEntryToData(pImageBase,
                                         bMappedAsImage,
                                         IMAGE_DIRECTORY_ENTRY_EXPORT,
                                         &dwExportSize);

    pImports = ImageDirectoryEntryToData(pImageBase,
                                         bMappedAsImage,
                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
                                         &dwImportSize);

    pResources = ImageDirectoryEntryToData(pImageBase,
                                           bMappedAsImage,
                                           IMAGE_DIRECTORY_ENTRY_RESOURCE,
                                           &dwResourceSize);

    if (pResources && dwResourceSize &&
        !pImports && !dwImportSize &&
        !pExports && !dwExportSize) {
        return (TRUE);
    } else {
        return (FALSE);
    }
}


BOOL
LogError(
        LPTSTR ErrMsg,
        PSYM_ERR pSymErr,
        UINT ErrNo )
{

    TCHAR szFName[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];
    TCHAR szCurName[_MAX_FNAME];


    if (pSymErr->ErrNo != 0) ErrNo = pSymErr->ErrNo;

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szCurName,szFName);
    _tcscat(szCurName,szExt);

    _tcscpy(ErrMsg,_T(""));

    // See if this is a file that we aren't supposed to report errors
    // for.  If it is, just return without writing an error.

    if ( pErrorFilterList != NULL ) {
        if ( InExcludeList( szCurName, pErrorFilterList ) ) {
            return(TRUE);
        }
    }

    switch (ErrNo) {
        case NO_DEBUG_DIRECTORIES:
            _stprintf(ErrMsg, "%-20s FAILED  - Built with no debugging information\n",
                      szCurName);
            return(TRUE);

        case NO_DEBUG_DIRECTORIES_IN_DBG_HEADER:
            _stprintf(ErrMsg, "%-20s FAILED  - Image header has no debugging information\n",
                      szCurName);
            return(TRUE);

        case NO_MISC_ENTRY:
            _stprintf(ErrMsg, "%-20s FAILED  - No MISC entry in debug directories\n",
                      szCurName);
            return(TRUE);

        case NO_FILE_IN_MISC:
            _stprintf(ErrMsg, "%-20s FAILED  - MISC entry contains no .dbg file information\n",
                      szCurName);
            return(TRUE);

        case ERROR_IF_SPLIT:
            _stprintf(ErrMsg, "%-20s FAILED  - Image points to a .DBG symbol file - fix with dbgtopdb.exe\n",
                      szCurName);
            return(TRUE);

        case ERROR_IF_NOT_SPLIT:
            _stprintf(ErrMsg, "%-20s FAILED  - Image contains .DBG file data - fix with dbgtopdb.exe\n",
                      szCurName);
            return(TRUE);

        case EXTRA_RAW_DATA_IN_6:
            _stprintf(ErrMsg, "%-20s FAILED  - No misc entry, but debug directories point to non-Codeview data\n",
                      szCurName );
            return (TRUE);

        case IGNORE_IF_SPLIT:
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image is already split\n",szCurName);
            }
            return(TRUE);


        case NO_CODE_VIEW:
            _stprintf(ErrMsg, "%-20s FAILED  - %s does not have a pointer to CodeView information\n",
                      szCurName, pSymErr->szSymbolFileName);
            return(TRUE);

        case FILE_NOT_FOUND:
            _stprintf(ErrMsg, "%-20s FAILED  - File does not exist\n",szCurName);
            return(TRUE);

        case CREATE_FILE_FAILED :
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image is not a valid NT image.\n", szCurName);
            }
            return(TRUE);

        case CREATE_FILE_MAPPING_FAILED :
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image is not a valid NT image.\n", szCurName);
            }
            return(TRUE);

        case MAPVIEWOFFILE_FAILED :
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image is not a valid NT image.\n", szCurName);
            }
            return(TRUE);

        case GET_FILE_INFO_FAILED :
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image is not a valid NT image.\n", szCurName);
            }
            return(TRUE);

        case HEADER_NOT_ON_LONG_BOUNDARY:
            // Not an NT image - ignore the error
            if (pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - This is either corrupt or a DOS image\n", szCurName);
            }
            return(TRUE);

        case IMAGE_BIGGER_THAN_FILE:
            if (pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - This is either corrupt or a DOS image\n", szCurName);
            }
            return(TRUE);

        case INVALID_ADDRESSOFRAWDATA_ZERO_DEBUG:
            _stprintf(ErrMsg, "%-20s FAILED  - Invalid AddressOfRawData for zero sized debug info\n",
                      szCurName);
            return(TRUE);

        case INVALID_POINTERTORAWDATA_NON_ZERO:
            _stprintf(ErrMsg, "%-20s FAILED  - Invalid PointerToRawData for non-zero sized debug info\n",
                      szCurName);
            return(TRUE);

        case INVALID_POINTERTORAWDATA_ZERO_DEBUG:
            _stprintf(ErrMsg, "%-20s FAILED  - Invalid PointerToRawData for zero sized debug info\n",
                      szCurName);
            return(TRUE);

        case NO_DOS_HEADER:
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image does not have a DOS header\n",
                          szCurName);
            }
            return(TRUE);

        case NOT_NT_IMAGE:
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Image does not have an NT header\n",
                          szCurName);
            }
            return(TRUE);
        case IMAGE_PASSED :
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s PASSED \n",szCurName);
            }
            return(TRUE);

        case RESOURCE_ONLY_DLL:
            if ( pSymErr->Verbose) {
                _stprintf(ErrMsg, "%-20s IGNORED - Resource only DLL\n",szCurName);
            }
            return(TRUE);

        case SIGNED_AND_NON_SPLIT:
            _stprintf(ErrMsg, "%-20s FAILED  - It is signed and debug info can't be stripped without invalidating the signature\n",
                      szCurName);
            return(TRUE);

        default:
            return(TRUE);
    }
    return (FALSE);
}


BOOL
LogDbgError(
           LPTSTR ErrMsg,
           PSYM_ERR pSymErr
           )

{

    TCHAR szFName[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];
    TCHAR szCurName[_MAX_FNAME];
    TCHAR szSymName[_MAX_FNAME];

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szCurName,szFName);
    _tcscat(szCurName,szExt);

    _tcscpy(ErrMsg,_T("") );

    // See if this is a file that we aren't supposed to report errors
    // for.  If it is, just return without writing an error.

    if ( pErrorFilterList != NULL ) {
        if ( InExcludeList( szCurName, pErrorFilterList ) ) {
            return(TRUE);
        }
    }

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szSymbolFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szSymName,szFName);
    _tcscat(szSymName,szExt);


    if ( !pSymErr->SymbolFileFound ) {
        _stprintf(ErrMsg, "%-20s FAILED  - Image is split correctly, but %s is missing\n",
                  szCurName, szSymName);
        return(TRUE);
    }

    if ( LogCheckSumErrors && !pSymErr->CheckSumsMatch) {
        _stprintf(ErrMsg, "%-20s FAILED  - Checksum doesn't match with %s\n",
                  szCurName, szSymName);
        return(TRUE);
    }

    if ( !pSymErr->SizeOfImageMatch) {
        _stprintf(ErrMsg, "%-20s FAILED  - Size of image doesn't match with %s\n",
                  szCurName, szSymName);
        return(TRUE);
    }

    if ( !pSymErr->TimeDateStampsMatch) {
        _stprintf(ErrMsg, "%-20s FAILED  - Timedate stamp doesn't match with %s\n",
                  szCurName, szSymName);
        return(TRUE);
    }

    return(TRUE);
}


BOOL
LogPdbError(
           LPTSTR ErrMsg,
           PSYM_ERR pSymErr
           )

{

    TCHAR szFName[_MAX_FNAME];
    TCHAR szExt[_MAX_EXT];
    TCHAR szCurName[_MAX_FNAME];
    TCHAR szSymName[_MAX_FNAME];
    TCHAR szPdbName[_MAX_FNAME];

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szCurName,szFName);
    _tcscat(szCurName,szExt);

    _tcscpy(ErrMsg,_T("") );

    // See if this is a file that we aren't supposed to report errors
    // for.  If it is, just return without writing an error.

    if ( pErrorFilterList != NULL ) {
        if ( InExcludeList( szCurName, pErrorFilterList ) ) {
            return(TRUE);
        }
    }

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szSymbolFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szSymName,szFName);
    _tcscat(szSymName,szExt);


    // Get the file name without any path info:
    _tsplitpath(pSymErr->szPdbFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szPdbName,szFName);
    _tcscat(szPdbName,szExt);

    if (pSymErr->ErrNo == NO_CODE_VIEW) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s does not point to CodeView information\n",
                  szCurName, szSymName);
        return(TRUE);
    }

    if (pSymErr->ErrNo == PRIVATE_INFO_NOT_REMOVED) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s contains private information\n",
                  szCurName, szPdbName);
        return(TRUE);
    }

    if (pSymErr->ErrNo == PDB_MAY_BE_CORRUPT) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s may be corrupt\n",
                  szCurName, szPdbName);
        return(TRUE);
    }

    if (pSymErr->ErrNo == CANNOT_LOAD_RSDS) {
        _stprintf(ErrMsg, "%-20s FAILED  - Cannot load the RSDS dll \n",
                  szCurName);
        return(TRUE);
    }

    if ( _tcscmp(pSymErr->szPdbErr,_T("") ) ) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s\n",szCurName, pSymErr->szPdbErr );
        return(TRUE);
    }

    if ( pSymErr->SymbolFileFound  && !pSymErr->PdbFileFound ) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s is correct, but %s is missing\n",
                  szCurName, szSymName, szPdbName);
        return(TRUE);
    }

    if ( !pSymErr->PdbFileFound ) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s is missing \n",szCurName,szPdbName );
        return(TRUE);
    }

    // There was a DBG file, but PDB file didn't validate

    if ( pSymErr->SymbolFileFound && !pSymErr->PdbValid ) {
        _stprintf(ErrMsg, "%-20s FAILED  - %s and %s signatures do not match\n",
                  szCurName, szSymName, szPdbName);
        return(TRUE);
    }

    // There isn't supposed to be a DBG file.  PDB doesn't validate against
    // image.

    if ( !pSymErr->PdbValid ) {
        _stprintf(ErrMsg, "%-20s FAILED  - signature does not match %s\n",
                  szCurName, szPdbName );
        return(TRUE);
    }
    return(FALSE);
}


BOOL AddToSymbolsCDLog(
                      FILE *hSymCDLog,
                      PSYM_ERR pSymErr,
                      LPTSTR szSymbolPath,
                      LPTSTR szFileExt
                      )

/*
   szSymbolPath is the full path and name to the symbol file
   szFileExt is the extension of the image without the '.' at
   the front
*/

{

    LPTSTR szSrc;
    LPTSTR szDest;
    LPTSTR szTmp;

    TCHAR szFName[_MAX_FNAME+1];
    TCHAR szExt[_MAX_EXT+1];
    TCHAR szCurName[_MAX_FNAME + _MAX_EXT + 1];
    TCHAR szDestDir[_MAX_PATH];
    TCHAR szSymName[_MAX_FNAME + 1];
    TCHAR szSymExt[_MAX_EXT + 1];

    // If there is a list of the files that belong on the
    // CD, then only write this file to the log for the
    // symbol CD if the file is in the list
    //
    // Originally, this was used for the international
    // incremental builds.
    //
    if ( pCDIncludeList != NULL ) {
        if ( !InList( szSymbolPath, pCDIncludeList ) ) {
            return (TRUE);
        }
    }

    // Get the file name without any path info:
    _tsplitpath(pSymErr->szFileName,NULL,NULL,szFName,szExt);
    _tcscpy(szCurName,szFName);


    // Put the path below "binaries" as the source
    szSrc = _tcsstr(szSymbolPath, _T("symbols\\") );

    if (szSrc == NULL) {
        printf("%s: Cannot find \"symbols\\\" in the symbol file's path\n",
               szCurName);
        exit(1);
    }
    if ( _tcscmp( szSrc, _T("symbols\\") )  == 0 ) {
        printf("Symbol file name cannot end with \"symbols\\\"\n");
        exit(1);
    }

    // Move the Destination up to the directory after symbols.  If this is
    // the retail directory, don't include retail in the path.
    szDest = szSrc + _tcslen(_T("symbols\\"));

    if ( _tcsncmp( szDest, _T("retail\\"), _tcslen(_T("retail\\")) ) == 0 ) {
        szDest = szDest + _tcslen(_T("retail\\"));
    }

    _tsplitpath(szDest,NULL,szDestDir,NULL,NULL);

    // Remove the '\' from the end of the string;
    szTmp = szDestDir + _tcslen(szDestDir) - 1;
    while ( _tcscmp( szTmp, _T("\\")) == 0 ) {
        _tcscpy( szTmp, _T("") );
        szTmp--;
    }

    // get the symbol file name
    _tsplitpath(szSymbolPath,NULL,NULL,szSymName,szSymExt);

    RemoveDuplicateSlashes(pSymErr->szFileName);

    fprintf(hSymCDLog, "%s,%s%s,%s,%s\n", pSymErr->szFileName,
            szSymName,szSymExt,szSrc,szDestDir);

    return (TRUE);
}



P_LIST
GetList(
       LPTSTR szFileName
       )

{

    /* GetList gets the list and keeps the original file name which could
     * have included the path to the file
     * Note, it can be merged with GetExcludeList.  I first created it for
     * use in creating the symbols CD, and didn't want to risk entering a
     * bug into symchk
     */

    P_LIST pList;

    FILE  *fFile;
    TCHAR szCurFile[_MAX_FNAME+1], *c;
    TCHAR fname[_MAX_FNAME+1], ext[_MAX_EXT+1];
    DWORD i;
    LPTSTR szEndName;


    if (  (fFile = _tfopen(szFileName,_T("r") )) == NULL )
    {
        // printf( "Cannot open the exclude file %s\n",szFileName );
        return NULL;
    }

    pList = (P_LIST)malloc(sizeof(LIST));

    if (pList)
    {
        pList->dNumFiles = 0;
        while ( _fgetts(szCurFile,_MAX_FNAME,fFile) )
        {
            if ( szCurFile[0] == ';' ) continue;
            (pList->dNumFiles)++;
        }

        // Go back to the beginning of the file
        fseek(fFile,0,0);
        pList->List = (LIST_ELEM*)malloc( sizeof(LIST_ELEM) *
                                          (pList->dNumFiles));
        if (pList->List == NULL)
        {
            free(pList);
            pList = NULL;
        }
        else
        {
            i = 0;
            while ( i < pList->dNumFiles )
            {
                memset(szCurFile,'\0',sizeof(TCHAR) * (_MAX_FNAME+1) );
                _fgetts(szCurFile,_MAX_FNAME,fFile);

                // Replace the \n with \0
                c = NULL;
                c  = _tcschr(szCurFile, '\n');
                if ( c != NULL) *c='\0';

                if ( szCurFile[0] == ';' ) continue;

                if ( _tcslen(szCurFile) > _MAX_FNAME ) {
                    printf("File %s has a string that is too large\n",szFileName);
                    free(pList->List);
                    free(pList);
                    fclose(fFile);
                    return(NULL);
                }

                // Allow for spaces and a ; after the file name
                // Move the '\0' back until it has erased the ';' and any
                // tabs and spaces that might come before it
                szEndName = _tcschr(szCurFile, ';');

                // Set the pointer to the last character in the string if
                // there wasn't a comment
                if (szEndName == NULL ) {
                    if (_tcslen(szCurFile) > 0 ) {
                        szEndName = szCurFile + _tcslen(szCurFile) - 1;
                    }
                }

                if (szEndName != NULL ) {
                    while ( *szEndName == ';' || *szEndName == ' '
                            || *szEndName == '\t' ) {
                        *szEndName = '\0';
                        if ( szEndName > szCurFile ) szEndName--;
                    }
                }

                _tcscpy(pList->List[i].Path,szCurFile);

                _tsplitpath(szCurFile,NULL,NULL,fname,ext);

                _tcscpy(pList->List[i].FName,fname);
                _tcscat(pList->List[i].FName,ext);

                i++;
            }

            // Sort the List
            dbg_qsort( (void*)pList->List, (size_t)pList->dNumFiles,
                   (size_t)sizeof(LIST_ELEM), SymComp2 );
        }
    }

    fclose(fFile);

    return (pList);
}

int __cdecl
SymComp2(
        const void *e1,
        const void *e2
        )
{
    LPTSTR p1,n1;
    LPTSTR p2,n2;
    int rc;

    p1 = ((LIST_ELEM*)e1)->FName;
    p2 = ((LIST_ELEM*)e2)->FName;

    n1 = ((LIST_ELEM*)e1)->Path;
    n2 = ((LIST_ELEM*)e2)->Path;

    rc = _tcsicmp(p1,p2);
    if (rc == 0) return ( _tcsicmp(n1,n2) );
    else return (rc);
}



BOOL PDBPrivateStripped(
                       PDB *ppdb,
                       DBI *pdbi
                       )
{
    // Return values:
    // FALSE - Private Information has NOT been stripped
    // TRUE - Private Information has been stripped

    Mod *pmod;
    Mod *prevmod; 
    long cb;

    unsigned itsm;
    TPI *ptpi;
    TI  tiMin;
    TI  tiMac;

    pmod = NULL;
    prevmod=NULL;
    while (DBIQueryNextMod(pdbi, pmod, &pmod) && pmod) {
        if (prevmod != NULL) ModClose(prevmod);

        // Check that Source line info is removed
        ModQueryLines(pmod, NULL, &cb);

        if (cb != 0) {
            ModClose(pmod);
            return FALSE;
        }

        // Check that local symbols are removed
        ModQuerySymbols(pmod, NULL, &cb);

        if (cb != 0) {
            ModClose(pmod);
            return FALSE;
        }
        prevmod=pmod;
    }
    if (pmod != NULL) ModClose(pmod);
    if (prevmod != NULL) ModClose(prevmod);

    // Check that types are removed
    for ( itsm = 0; itsm < 256; itsm++) {
        ptpi = 0;
        if (DBIQueryTypeServer(pdbi, (ITSM) itsm, &ptpi)) {
            continue;
        }
        if (!ptpi) {

            PDBOpenTpi(ppdb, pdbRead, &ptpi);
            tiMin = TypesQueryTiMinEx(ptpi);
            tiMac = TypesQueryTiMacEx(ptpi);
            if (tiMin < tiMac) {
                TypesClose(ptpi);
                return FALSE;
            }
        }
    }
    TypesClose(ptpi);
    return (TRUE);
}

#if defined(use_SymutilX)

BOOL PDBPrivateStrippedX(
                       PDB *ppdb,
                       DBI *pdbi
                       )
{
    // Return values:
    // FALSE - Private Information has NOT been stripped
    // TRUE - Private Information has been stripped

    Mod *pmod;
    Mod *prevmod;
    long cb;

    unsigned itsm;
    TPI *ptpi;
    TI  tiMin;
    TI  tiMac;

    pmod = NULL;
    prevmod=NULL;
    while (pDBIQueryNextMod(pdbi, pmod, &pmod) && pmod) {
        if (prevmod != NULL) pModClose(prevmod);

        // Check that Source line info is removed
        pModQueryLines(pmod, NULL, &cb);

        if (cb != 0) {
            pModClose(pmod);
            return FALSE;
        }

        // Check that local symbols are removed
        pModQuerySymbols(pmod, NULL, &cb);

        if (cb != 0) {
            pModClose(pmod);
            return FALSE;
        }
        prevmod=pmod;
    }
    if (pmod != NULL) pModClose(pmod);
    if (prevmod != NULL) pModClose(prevmod);

    // Check that types are removed
    for ( itsm = 0; itsm < 256; itsm++) {
        ptpi = 0;
        if (pDBIQueryTypeServer(pdbi, (ITSM) itsm, &ptpi)) {
            continue;
        }
        if (!ptpi) {

            pPDBOpenTpi(ppdb, pdbRead, &ptpi);
            tiMin = pTypesQueryTiMinEx(ptpi);
            tiMac = pTypesQueryTiMacEx(ptpi);
            if (tiMin < tiMac) {
                pTypesClose(ptpi);
                return FALSE;
            }
        }
    }
    pTypesClose(ptpi);
    return (TRUE);
}

#endif


BOOL
RemoveDuplicateSlashes(
   LPTSTR Str
) {

ULONG i,j;
BOOL prev;
LPTSTR cur;

   prev = FALSE;   // False means the previous character was not a '\'
                   // True means the previous character was a '\'

   j = _tcslen(Str);
   if (j == 0 ) return TRUE;
   cur = Str;

   for (i=0; i< j; i++ ) {
      if ( *(Str+i) == _T('\\') ) {

        if ( prev == FALSE ) {

           prev=TRUE;
           *cur=*(Str+i);
           cur++;

        } else {

           // do nothing 
        }

      } else {

           prev=FALSE;
           *cur=*(Str+i);
           cur++;
      }
   }

   *cur=_T('\0');
   return(TRUE);
}


BOOL
ansi2wcs(
    PSTR  psz,
    PWSTR pwsz,
    DWORD pwszlen
    )
{
    BOOL rc;
    int  len;

    assert(psz && pwsz);

    len = strlen(psz);
    if (!len)
        return FALSE;

    rc = MultiByteToWideChar(CP_ACP,
                             MB_COMPOSITE,
                             psz,
                             len,
                             pwsz,
                             pwszlen);
    if (!rc)
        return FALSE;

    pwsz[len] = 0;

    return TRUE;
}