#include "symutil.h" typedef struct _FILE_INFO { DWORD TimeDateStamp; DWORD CheckSum; TCHAR szName[MAX_PATH]; } FILE_INFO, *PFILE_INFO; typedef struct _COMMAND_ARGS { LPTSTR szDir; // Directory where source files exist LPTSTR szFileName; // File name(s) to copy FILE * hSymCDLog; // SymbolsCD log file BOOL Recurse; // Recurse in subdirectories LPTSTR szSymPath; // Directory where symbols exist LPTSTR szExcludeFileName; // File name with list of files to exclude // from symbol checking LPTSTR szListFileName; // File containing a list of files to check DWORD Split; // TRUE - check for split images // FALSE - check for non-split images BOOL Verbose; // Print info for every file checked, // not just the ones that fail LPTSTR szErrorFilterList; // Don't print errors for these files LPTSTR szCDIncludeList; // Full path to symbols that should get // written to the list that is used for creating // the symbol CD. Originally used for // international incremental builds } COM_ARGS, *PCOM_ARGS; typedef struct _FILE_COUNTS { DWORD NumPassedFiles; DWORD NumIgnoredFiles; DWORD NumFailedFiles; } FILE_COUNTS, *PFILE_COUNTS; // Prototypes PCOM_ARGS GetCommandLineArgs( int argc, char **argv ); VOID Usage ( VOID ); DWORD CheckDirectory( LPTSTR szDir, LPTSTR szFName, LPTSTR szSymPath, FILE* hSymCDLog, PEXCLUDE_LIST pExcludeList, PFILE_COUNTS pFileCounts, DWORD Split ); DWORD CheckAllDirectories( LPTSTR szDir, LPTSTR szFName, LPTSTR szSymPath, FILE* hSymCDLog, PEXCLUDE_LIST pExcludeList, PFILE_COUNTS pFileCounts, DWORD Split ); BOOL CorrectPath( LPTSTR szFileName, LPTSTR szPathName, LPTSTR szCorrectPath ); // Global variables BOOL Verbose = 0; BOOL Retail = TRUE; int _cdecl main( int argc, char **argv) { PCOM_ARGS pArgs; DWORD NumBadFiles=0; DWORD NumExcludeFiles=0; DWORD i; PEXCLUDE_LIST pExcludeList = NULL; FILE_COUNTS FileCounts; SYM_ERR SymErr; TCHAR ErrMsg[MAX_SYM_ERR]; HFILE hListFile; P_LIST FileList; pArgs = GetCommandLineArgs(argc, argv); Verbose = (BOOL)pArgs->Verbose; memset( &SymErr, 0, sizeof(SymErr) ); memset( &FileCounts, 0, sizeof(FILE_COUNTS) ); if ( pArgs->szExcludeFileName != NULL ) { pExcludeList = GetExcludeList(pArgs->szExcludeFileName); } if ( pArgs->szErrorFilterList != NULL ) { pErrorFilterList = GetExcludeList(pArgs->szErrorFilterList); } if ( pArgs->szCDIncludeList != NULL ) { pCDIncludeList = GetList( pArgs->szCDIncludeList ); } // This is the section for creating the symbols CD. if ( pArgs->szListFileName != NULL ) { FileList = GetList(pArgs->szListFileName); if ( FileList == NULL ) { printf(" Cannot open the file list %s\n", pArgs->szListFileName); exit(1); } if ( FileList->dNumFiles == 0 ) goto finish; // Do the first one, so we don't have to check for it inside the loop if ( CorrectPath(FileList->List[0].FName, FileList->List[0].Path, pArgs->szDir) ) { NumBadFiles += CheckDirectory( pArgs->szDir, FileList->List[0].FName, pArgs->szSymPath, pArgs->hSymCDLog, pExcludeList, &FileCounts, pArgs->Split ); } for ( i=1; i< FileList->dNumFiles; i++) { // There may be some duplicates in the list ... skip them // Also, only check the files that are in the path given on the command line if ( (_tcsicmp(FileList->List[i].Path, FileList->List[i-1].Path) != 0) && CorrectPath(FileList->List[i].FName, FileList->List[i].Path, pArgs->szDir) ) { NumBadFiles += CheckDirectory( pArgs->szDir, FileList->List[i].FName, pArgs->szSymPath, pArgs->hSymCDLog, pExcludeList, &FileCounts, pArgs->Split ); } } } else { if ( !pArgs->Recurse ) { NumBadFiles += CheckDirectory( pArgs->szDir, pArgs->szFileName, pArgs->szSymPath, pArgs->hSymCDLog, pExcludeList, &FileCounts, pArgs->Split ); } else { NumBadFiles += CheckAllDirectories( pArgs->szDir, pArgs->szFileName, pArgs->szSymPath, pArgs->hSymCDLog, pExcludeList, &FileCounts, pArgs->Split ); } // CheckDirectory just returns the number of failed and passed. If // no files failed or passed, then report that we couldn't find the // file. if ( (FileCounts.NumFailedFiles + FileCounts.NumPassedFiles) == 0 ) { _tcscpy( SymErr.szFileName, pArgs->szFileName ); SymErr.Verbose=pArgs->Verbose; if (InExcludeList(SymErr.szFileName, pExcludeList) ) { LogError(ErrMsg, &SymErr, IMAGE_PASSED ); FileCounts.NumPassedFiles=1; } else { LogError(ErrMsg, &SymErr, FILE_NOT_FOUND); FileCounts.NumFailedFiles=1; } if ( _tcscmp(ErrMsg, "") != 0 ) { printf("SYMCHK: %s",ErrMsg); } } } finish: if (pArgs->hSymCDLog) fclose(pArgs->hSymCDLog); free(pArgs->szDir); free(pArgs->szFileName); free(pArgs); printf("\nSYMCHK: FAILED files = %d\n",FileCounts.NumFailedFiles); printf("SYMCHK: PASSED + IGNORED files = %d\n",FileCounts.NumPassedFiles); if ( FileCounts.NumFailedFiles > 0 ) { return(1); } else { return(0); } } DWORD CheckAllDirectories( LPTSTR szDir, LPTSTR szFName, LPTSTR szSymPath, FILE* hSymCDLog, PEXCLUDE_LIST pExcludeList, PFILE_COUNTS pFileCounts, DWORD Split ) { HANDLE hFindFile; TCHAR szCurPath[_MAX_PATH]; BOOL Found = FALSE; DWORD NumBadFiles=0; WIN32_FIND_DATA FindFileData; LPTSTR szF = NULL; LPTSTR szE = NULL; FILE_COUNTS FileCounts; NumBadFiles += CheckDirectory(szDir, szFName, szSymPath, hSymCDLog, pExcludeList, pFileCounts, Split ); // Look for all the subdirectories _tcscpy(szCurPath, szDir); _tcscat(szCurPath, _T("\\*.*") ); Found = TRUE; hFindFile = FindFirstFile((LPCTSTR)szCurPath, &FindFileData); if ( hFindFile == INVALID_HANDLE_VALUE) { Found = FALSE; } while ( Found ) { if ( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if ( !_tcscmp(FindFileData.cFileName, _T(".")) || !_tcscmp(FindFileData.cFileName, _T("..")) || !_tcsicmp(FindFileData.cFileName, _T("symbols")) ) { // Don't process these directories } else { // Get the current path that we are searching in _tcscpy(szCurPath, szDir); _tcscat(szCurPath, _T("\\")); _tcscat(szCurPath, FindFileData.cFileName); NumBadFiles += CheckAllDirectories( szCurPath, szFName, szSymPath, hSymCDLog, pExcludeList, pFileCounts, Split ); } } Found = FindNextFile(hFindFile, &FindFileData); } FindClose(hFindFile); return(NumBadFiles); } DWORD CheckDirectory( LPTSTR szDir, LPTSTR szFName, LPTSTR szSymPath, FILE * hSymCDLog, PEXCLUDE_LIST pExcludeList, PFILE_COUNTS pFileCounts, DWORD Split ) { HANDLE hFindFile; TCHAR szFileName[_MAX_PATH]; TCHAR szCurPath[_MAX_PATH]; TCHAR szCurFileName[_MAX_PATH]; BOOL Found; DWORD NumBadFiles=0; DWORD NumGoodFiles=0; WIN32_FIND_DATA FindFileData; SYM_ERR SymErr; TCHAR ErrMsg[MAX_SYM_ERR]; memset( &SymErr, 0, sizeof(SymErr) ); // Create the file name _tcscpy(szFileName, szDir); _tcscat(szFileName, _T("\\") ); _tcscat(szFileName, szFName); // Get the current path that we are searching in _tcscpy(szCurPath, szDir); Found = TRUE; hFindFile = FindFirstFile((LPCTSTR)szFileName, &FindFileData); if ( hFindFile == INVALID_HANDLE_VALUE ) { Found = FALSE; } while ( Found ) { // Found a file, not a directory if ( !(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) { _tcscpy(szCurFileName, szCurPath); _tcscat(szCurFileName,_T("\\") ); _tcscat(szCurFileName, FindFileData.cFileName ); // Its not in the exclude list, go ahead and test it if (!InExcludeList(FindFileData.cFileName,pExcludeList ) ) { if ( !CheckSymbols( ErrMsg, szSymPath, szCurFileName, hSymCDLog, Split, Verbose, NULL ) ) { pFileCounts->NumFailedFiles++; NumBadFiles++; } else { pFileCounts->NumPassedFiles++; } } // It is in the exclude list, add it to NumPassed Files else { pFileCounts->NumPassedFiles++; _tcscpy(SymErr.szFileName, szCurFileName); SymErr.Verbose = Verbose; LogError(ErrMsg, &SymErr, IMAGE_PASSED); } if ( _tcscmp(ErrMsg,"") != 0 ) { printf("SYMCHK: %s", ErrMsg); } } Found = FindNextFile(hFindFile, &FindFileData); } FindClose(hFindFile); return(NumBadFiles); } VOID Usage ( VOID ) { puts("\n" "Usage: symchk [switches] file /s sympath \n\n" " file Name of file(s) or directory to check.\n" " Can include wildcards.\n\n" " [/b] For NT4 Service Packs -- don't complain if\n" " there is no CodeView data\n" " [/e file] File containing a list of files to exclude.\n" " This file can have one name per line. Comment\n" " lines begin with a ; . \n\n" " [/p] Check that private information is removed.\n" " [/c dest] Create an inf for the symbols CD\n\n" " [/r] Recurse into subdirectories. This uses the\n" " Windows 2000 build model and assumes that the\n" " first subdirectory to traverse into is the retail\n" " directory. Example sympath: \"E:\\binaries\\symbols\" .\n" " All subdirectories except \"symbols\" will be checked.\n\n" " /s sympath symbol path delimited by ; . Checks sympath\n" " and sympath\\ext for each string in the path, \n" " where ext is the extension of the executable.\n\n" " [/t] Fail if a DBG file is involved. Fails if the image points\n" " to a dbg file or if it contains data that can be stripped\n" " into a dbg file. Default is to fail if data can be stripped\n" " into a dbg file, but don't fail if the image points to a\n" " dbg file.\n\n" " [/u] Fail if image points to a dbg file. Don't fail if image\n" " contains data that can be split into a dbg file.\n\n" " [/v] Give verbose information\n" " [/x] Used with /c. Perform symbol checking on these files and\n" " add the correct symbols to the symbol CD's inf, but\n" " don't write error messages for the ones that are wrong.\n\n" ); exit(1); // The purpose of /x is to not log errors for symbols in symbad.txt. However, symchk // should check all of the files in symbad.txt when it is creating the list of file // in case some of them actually have correct symbols and symbad hasn't been updated yet. } PCOM_ARGS GetCommandLineArgs( int argc, char **argv ) { PCOM_ARGS pArgs; int i,cur,length; TCHAR c; BOOL NeedSecond = FALSE; BOOL Exclude = FALSE; LPTSTR szFileArg = NULL; TCHAR szDrive[_MAX_DRIVE + 1]; TCHAR szDir[_MAX_DIR + 1]; TCHAR szFileName[_MAX_FNAME + 1]; TCHAR szExt[_MAX_EXT + 1]; TCHAR szNameExt[_MAX_FNAME + _MAX_EXT + 1]; LPTSTR szSymCDLog = NULL; LPTSTR szSymbolsCDFile = NULL; HANDLE fHandle; WIN32_FIND_DATA FindFileData; if (argc == 1) Usage(); if (!(pArgs = (PCOM_ARGS)malloc(sizeof(COM_ARGS)))) { printf("No memory"); exit(1); } memset( pArgs, 0, sizeof(COM_ARGS) ); pArgs->Split = 0; pArgs->szListFileName = NULL; pArgs->szCDIncludeList = NULL; CheckPrivate = FALSE; for (i=1; i cur) Usage(); break; case 'l': NeedSecond = TRUE; break; case 'p': NeedSecond = FALSE; CheckPrivate = TRUE; break; case 'r': pArgs->Recurse = TRUE; Recurse = TRUE; break; case 's': NeedSecond = TRUE; if ( length > cur) Usage(); break; case 't': pArgs->Split |= ERROR_IF_NOT_SPLIT; pArgs->Split |= ERROR_IF_SPLIT; break; case 'u': pArgs->Split |= ERROR_IF_SPLIT; break; case 'v': pArgs->Verbose = TRUE; break; case 'x': NeedSecond = TRUE; break; case 'y': NeedSecond = TRUE; break; default: Usage(); } } } else { if (szFileArg != NULL) Usage(); szFileArg = argv[i]; } } else { NeedSecond = FALSE; switch (c) { case 'c': szSymbolsCDFile = argv[i]; break; case 'e': pArgs->szExcludeFileName = argv[i]; break; case 'l': pArgs->szListFileName = argv[i]; break; case 's': pArgs->szSymPath = argv[i]; break; case 'x': pArgs->szErrorFilterList = argv[i]; break; case 'y': pArgs->szCDIncludeList = argv[i]; break; default: Usage(); } } } if ( pArgs->Split == 0 ) { // This has always been the default behavior pArgs->Split = ERROR_IF_NOT_SPLIT; } if ( szFileArg == NULL ) Usage(); // make the Symbol Copy log for the Support Tools CD if ( szSymbolsCDFile != NULL ) { if ( (pArgs->hSymCDLog = fopen(szSymbolsCDFile, "a+")) == NULL ) { printf("Cannot open %s for appending\n",szSymbolsCDFile); exit(1); } } // Get the filenames so they are correct _tsplitpath( szFileArg, szDrive, szDir, szFileName, szExt ); // Get current directory if they didn't enter a directory if ( !_tcscmp(szDrive, "") && !_tcscmp(szDir,"") ) { GetCurrentDirectory(_MAX_DIR, szDir); } // If szFileName and szExt are "" then put *.* in them if ( !_tcscmp(szFileName,"") && !_tcscmp(szExt,"") ) { _tcscpy(szFileName,"*"); } // User may have entered a directory with an implies * for the // file name. fHandle = FindFirstFile( szFileArg, &FindFileData ); _tcscpy(szNameExt, szFileName); _tcscat(szNameExt, szExt); // If its a directory and the name of the directory matches // the filename.ext from the command line parameter, then the user // entered a directory, so add * to the end. if ( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (_tcscmp( szNameExt, FindFileData.cFileName )== 0) ) { // Move the filename to be the dir _tcscat( szDir, "\\"); _tcscat( szDir, szFileName); // Put the file name as * _tcscpy(szFileName, "*"); } pArgs->szDir=(LPTSTR) malloc( sizeof(TCHAR)* _MAX_PATH + 1 ); _tmakepath( pArgs->szDir, szDrive, szDir, NULL, NULL); pArgs->szFileName = (LPTSTR) malloc( sizeof(TCHAR) * _MAX_PATH + 1 ); _tmakepath(pArgs->szFileName, NULL, NULL, szFileName, szExt); // Check that everything has been entered if (NeedSecond || (pArgs->szFileName == NULL) || (pArgs->szDir == NULL) || (pArgs->szSymPath == NULL) ) { Usage(); } return (pArgs); } BOOL CorrectPath( LPTSTR szFileName, LPTSTR szPathName, LPTSTR szCorrectPath ) { // To return TRUE, szPathName should equal szCorrectPath + \ + szFileName // The only hitch is that there could be extraneous \'s TCHAR CorrectPathx[_MAX_PATH + _MAX_FNAME + _MAX_EXT + 1]; TCHAR PathNamex[_MAX_PATH + _MAX_FNAME + _MAX_EXT + 1]; LONG length, index, i; // Get rid of any extra \'s length = _tcslen(szPathName); PathNamex[0] = szPathName[0]; index = 1; for (i=1; i<=length; i++) { if ( (szPathName[i-1] != '\\') || (szPathName[i] != '\\') ) { PathNamex[index] = szPathName[i]; index++; } } length = _tcslen(szCorrectPath); CorrectPathx[0] = szCorrectPath[0]; index = 1; for (i=1; i<=length; i++) { if ( (szCorrectPath[i-1] != '\\') || (szCorrectPath[i] != '\\') ) { CorrectPathx[index] = szCorrectPath[i]; index++; } } // Make sure that the correct path doesn't end in a '\' length = _tcslen(CorrectPathx); if ( CorrectPathx[length-1] == '\\' ) CorrectPathx[length-1] = '\0'; _tcscat(CorrectPathx,"\\"); _tcscat(CorrectPathx,szFileName); if ( _tcsicmp(CorrectPathx, szPathName) == 0) return TRUE; else return FALSE; }