windows-nt/Source/XPSP1/NT/sdktools/chksum/chksum.c

553 lines
18 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/**************************************************************************************\
* Chksum.c
* Purpose: Print to stdout checksum of all files in current directory, and optionally
* recurse from current directory.
*
* Created 02-15-95. DonBr
*
\**************************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <errno.h>
#include <string.h>
#include <io.h>
#include <imagehlp.h>
#include <direct.h>
#include <ctype.h>
// type definitions
#define exename "chksum"
#define MAX_EXCLUDE (30)
#define LISTSIZE 12000 // max allowable number of files and dirs in a flat directory
typedef struct List {
char Name[MAX_PATH]; // file or directory name
unsigned long Attributes;
unsigned long Size;
} List, *pList;
// Function prototypes
VOID CheckRel();
VOID CheckSum(List *rgpList, TCHAR *x); //, TCHAR *szDirectory);
int __cdecl CompFileAndDir( const void *elem1 , const void *elem2);
int __cdecl CompName( const void *elem1 , const void *elem2);
int MyGetFullPathName( IN const CHAR *InPath, IN OUT CHAR *FullPath);
VOID CreateOutputPath(char *CurrentDir, char *NewDir);
VOID ParseArgs(int *pargc, char **argv);
VOID Usage();
// Variable declarations
BOOL fRecurse = FALSE;
BOOL fPathOverride = FALSE;
BOOL fFileOut = FALSE;
BOOL fExclude = FALSE;
BOOL fFileIn = FALSE;
int DirNum = 1,DirNameSize = 0, ProcessedFiles=0, endchar=0, ExclCounter=0;
int grc=0; // global return code
char szRootDir[MAX_PATH];
char szRootDir2[MAX_PATH];
char *szFileOut;
char szFileOutFullPath[MAX_PATH];
char *szExclude[MAX_EXCLUDE];
char *szFileIn;
CHAR szDirectory[MAX_PATH] = {"."}; // default to current directory
FILE* fout;
FILE* fin;
// Begin main program
VOID __cdecl
main(
INT argc,
LPSTR argv[]
)
{
TCHAR CWD[MAX_PATH];
HANDLE logfh;
ParseArgs(&argc, argv);
// Create File if fFileOut==TRUE
if (fFileOut) {
fout = fopen(szFileOut, "w");
if (fout == NULL) {
fprintf(stderr, "Output file %s could not be created.\n", szFileOut);
exit(1);
}
}
/*
// Open File if fFileIn==TRUE
if (fFileIn) {
fin = fopen(szFileIn, "r"); // open check file
if (fin == NULL) {
fprintf(stderr, "Check file %s could not be opened.\n", szFileIn);
exit(1);
}
}
*/
// set root path
if (fPathOverride) {
// attempt to change directories
if (_chdir(szRootDir) == -1){
fprintf(stderr, "Path not found: %s\n", szRootDir);
Usage();
}
}else{
GetCurrentDirectory(MAX_PATH, szRootDir);
}
fprintf(fout==NULL? stdout : fout , "Processing %s\n", szRootDir);
CheckRel(); // primary worker routine
fprintf(stdout, "%d files processed in %d directories\n", ProcessedFiles, DirNum);
if (fFileOut) {
fclose(fout);
}
exit(grc);
}
/**************************************************************************************\
* Checkrel
* Purpose: Create an array of List structures containing file data for the current
* directory, sort the array alphabetically placing Files first and
* directories last, and finally process the array contents. Another instance
* of checkrel is started for directories, and checksum is called for files.
\**************************************************************************************/
VOID CheckRel()
{
HANDLE fh;
TCHAR CurrentDir[MAX_PATH] = {"\0"};
TCHAR NewDir[MAX_PATH] = {"\0"};
WIN32_FIND_DATA *pfdata;
BOOL fFilesInDir=FALSE;
BOOL fDirsFound=FALSE;
int iArrayMember=0, cNumDir=0, i=0, Length=0;
pList *rgpList = NULL; // a pointer to an array of pointers to List structures
CHAR cFileNameFullPath[MAX_PATH];
pfdata = (WIN32_FIND_DATA*)malloc(sizeof(WIN32_FIND_DATA));
if (!pfdata) {
fprintf(stderr, "Not enough memory.\n");
grc++;
return;
}
// Find the first file
fh = FindFirstFile("*.*", pfdata);
if (fh == INVALID_HANDLE_VALUE) {
fprintf(fout==NULL? stdout : fout , "\t No files found\n");
free(pfdata);
grc++;
return;
}
// Allocate an array of pointers to List structures
rgpList = (pList *) malloc(LISTSIZE * sizeof(pList));
if (!rgpList) {
fprintf(stderr, "Not enough memory allocating rgpList[].\n");
free(pfdata);
FindClose(fh); // close the file handle
grc++;
return;
}
//
// DoWhile loop to find all files and directories in current directory
// and copy pertinent data to individual List structures.
//
do { // while (FindNextFile(fh, pfdata))
if (strcmp(pfdata->cFileName, ".") && strcmp(pfdata->cFileName, "..")) { // skip . and ..
//
// If excluding files and current file matches any excluded file
// (case insensitively), then don't process current file
//
if (fExclude) {
for (i=0; i < ExclCounter; i++) {
if (!_strcmpi(pfdata->cFileName, szExclude[i])) {
goto excludefound;
}
}
}
//
// If current file matches output file name, then don't process current file
//
if ((fFileOut) && (!strcmp(szFileOut, pfdata->cFileName)) ) {
// File names match. If full paths match, ignore the output file.
MyGetFullPathName(pfdata->cFileName, cFileNameFullPath);
if ( !_strcmpi( szFileOutFullPath, cFileNameFullPath) ) {
goto excludefound;
}
}
rgpList[iArrayMember] = (pList)malloc(sizeof(List)); // allocate the memory
if (!rgpList[iArrayMember]) {
fputs("Not enough memory.\n", stderr);
free(pfdata);
FindClose(fh); // close the file handle
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
free(rgpList);
grc++;
return;
}
strcpy(rgpList[iArrayMember]->Name, pfdata->cFileName);
_strlwr(rgpList[iArrayMember]->Name); // all lowercase for strcmp in CompName
memcpy(&(rgpList[iArrayMember]->Attributes), &pfdata->dwFileAttributes, 4);
memcpy(&(rgpList[iArrayMember]->Size), &pfdata->nFileSizeLow, 4);
if (!(rgpList[iArrayMember]->Attributes & FILE_ATTRIBUTE_DIRECTORY)) { //If file
fFilesInDir=TRUE;
} else {
if (rgpList[iArrayMember]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { //If directory
fDirsFound=TRUE;
}
if (fRecurse) { // if recursive increment directory counter
cNumDir++;
}
}
iArrayMember++;
if (iArrayMember >= LISTSIZE) {
GetCurrentDirectory(MAX_PATH, CurrentDir);
fprintf(stderr, "More than %d files in %s. \nRebuild chksum.exe or eliminate some files from the root of this directory.\n", LISTSIZE, CurrentDir);
free(pfdata);
FindClose(fh); // close the file handle
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
free(rgpList);
grc++;
return;
}
excludefound: ;
}
} while (FindNextFile(fh, pfdata));
if (pfdata) free(pfdata);
if (fh) FindClose(fh); // close the file handle
//
// if no directories or files found with exception of . and ..
//
if ( (iArrayMember==0) || (!fFilesInDir) ){
GetCurrentDirectory(MAX_PATH, CurrentDir);
CreateOutputPath(CurrentDir, NewDir);
// fprintf(fout==NULL? stdout : fout , "%s - No files\n", NewDir);
}
// Sort Array arranging FILE entries at top
qsort( (void *)rgpList, iArrayMember, sizeof(List *), CompFileAndDir);
// Sort Array alphabetizing only FILE names
qsort( (void *)rgpList, iArrayMember-cNumDir, sizeof(List *), CompName);
// Sort Array alphabetizing only DIRectory names
qsort( (void *)&rgpList[iArrayMember-cNumDir], cNumDir, sizeof(List *), CompName);
//
// Process newly sorted structures.
// Checksum files or start another instance of checkrel() for directories
//
for (i=0; i < iArrayMember; ++i) {
if (rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { // if Dir
if (fRecurse) { // if recursive
if (_chdir(rgpList[i]->Name) == -1){ // cd into subdir and check for error
fprintf(stderr, "Unable to change directory: %s (error %d)\n", rgpList[i]->Name, GetLastError());
grc++;
} else {
DirNum++; // directory counter
CheckRel(); // start another iteration of checkrel function in new directory
_chdir(".."); // get back to previous directory when above iteration returns
} // end if _chdir
} // end if recurse
} else { // else if not Directory
GetCurrentDirectory(MAX_PATH, CurrentDir);
CreateOutputPath(CurrentDir, NewDir);
CheckSum(rgpList[i], NewDir);
}
} // end for i < iArrayMember
// Clean up the array and it's elements
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
free(rgpList);
} // end CheckRel
/*************************************************************************************\
* CheckSum
* Purpose: uses MapFileAndCheckSum to determine file checksum and outputs data.
\*************************************************************************************/
VOID CheckSum(List *rgpList, TCHAR *x) {//TCHAR *szDirectory) {
ULONG HeaderSum, CheckSum=0, status;
if (rgpList->Size != 0) { //High != 0 || rgpList->nFileSizeLow != 0) {
status = MapFileAndCheckSum(rgpList->Name, &HeaderSum, &CheckSum);
if (status != CHECKSUM_SUCCESS) {
fprintf(fout==NULL? stdout : fout , "\nCannot open or map file: %s (error %d)\n", rgpList->Name, GetLastError());
grc++;
return;
}
}
fprintf(fout==NULL? stdout : fout , "%s\\%s %lx\n", x, rgpList->Name, CheckSum);//szDirectory, rgpList->Name, CheckSum);
ProcessedFiles++;
} //CheckSum
/********************************************************************************************\
* CompFileAndDir
* Purpose: a comparision routine passed to QSort. It compares elem1 and elem2
* based upon their attribute, i.e., is it a file or directory.
\********************************************************************************************/
int __cdecl
CompFileAndDir( const void *elem1 , const void *elem2 )
{
pList p1, p2;
// qsort passes a void universal pointer, use a typecast (List**)
// so the compiler recognizes the data as a List structure.
// Typecast pointer-to-pointer-to-List and dereference ONCE
// leaving a pList. I don't dereference the remaining pointer
// in the p1 and p2 definitions to avoid copying the structure.
p1 = (*(List**)elem1);
p2 = (*(List**)elem2);
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
return 0;
} //both dirs
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
return 0;
} //both files
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
return 1;
} // elem1 is dir and elem2 is file
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
return -1;
} // elem1 is file and elem2 is dir
return 0; // if none of above
}
/********************************************************************************************\
* CompName is another compare routine passed to QSort that compares the two Name strings *
\********************************************************************************************/
int __cdecl
CompName( const void *elem1 , const void *elem2 )
{
return strcmp( (*(List**)elem1)->Name, (*(List**)elem2)->Name );
}
/**********************************************************************************************\
* CreateOutputPath just formats NewDir, the path prepended to filename during checksum output *
\**********************************************************************************************/
VOID CreateOutputPath(char *CurrentDir, char *NewDir)
{
strcpy(NewDir, ".");
// if rootdir ends in '\' and currentdir and szrootdir2 don't match
// handles case where /p path override arg ends in a '\' char like "/p g:\"
// files listed at the root don't need the extra '\' placed in NewDir, but
// directories at the root DO need ".\" prepended to their name
_strlwr(CurrentDir);
//fprintf(stdout, "szrootdir: %s, szrootdir2: %s, currentdir: %s\n", szRootDir, szRootDir2, CurrentDir);
if ( (szRootDir[strlen(szRootDir)-1] == '\\') && // if arg path ends in "\"
(strcmp(CurrentDir, szRootDir2)) && // if they don't match
(CurrentDir[strlen(CurrentDir)-1] != '\\') //&& // if currentdir doesn't end with "\"
//(szRootDir2[strlen(szRootDir2)-1] != ':') ){ // if arg path ends with ":"
){
strcat(NewDir, "\\");
}
if ( (CurrentDir[strlen(CurrentDir)-2] !=':') && (CurrentDir[strlen(CurrentDir)-1] !='\\') ){
strcat(NewDir, &CurrentDir[(strlen(szRootDir))] );
}
}
VOID
ParseArgs(int *pargc, char **argv) {
CHAR cswitch, c, *p;
int argnum = 1;
while ( argnum < *pargc ) {
_strlwr(argv[argnum]);
cswitch = *argv[argnum];
if (cswitch == '/' || cswitch == '-') {
c = *(argv[argnum]+1);
switch (c) {
case '?':
Usage();
case 'r':
fRecurse = TRUE;
break;
case 'p':
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
++argnum; // increment to next arg string
strcpy(szRootDir, argv[argnum]);
if (szRootDir == NULL) {
fprintf(stderr, "out of memory for root dir.\n");
exit(1);
}
fPathOverride = TRUE;
// Find the full path to the root
if ( !MyGetFullPathName(argv[argnum], szRootDir) ) {
fprintf(stderr, "Cannot get full path for root dir %s\n", szRootDir);
exit(1);
}
_strlwr(szRootDir);
strcpy(szRootDir2, szRootDir);
// if path given ends in a "\", remove it...
if (szRootDir2[strlen(szRootDir2)-1] == 92) szRootDir2[strlen(szRootDir2)-1] = '\0';
break;
} else {
Usage();
}
case 'o':
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
++argnum;
szFileOut = _strdup(argv[argnum]);
if (szFileOut == NULL) {
fprintf(stderr, "Out of memory for output file.\n");
exit(1);
}
fFileOut = TRUE;
// Find the full path to the output file
if ( !MyGetFullPathName(szFileOut, szFileOutFullPath) ) {
fprintf(stderr, "Cannot get full path for output file %s\n", szFileOut);
exit(1);
}
_strlwr(szFileOutFullPath); // lower case full path to output file
_strlwr(szFileOut); // lower case path to output file
break;
} else {
Usage();
}
case 'x': // check number of args given
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
++argnum;
szExclude[ExclCounter] = _strdup(argv[argnum]);
if (szExclude[ExclCounter] == NULL) {
fprintf(stderr, "Out of memory for exclude name.\n");
exit(1);
}
fExclude = TRUE;
_strlwr(szExclude[ExclCounter]);
ExclCounter++;
break;
} else {
Usage();
}
/*
case 'i':
if ( (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
++argnum;
szFileIn = strdup(argv[argnum]);
if (szFileIn == NULL) {
fprintf(stderr, "Out of memory for input file.\n");
exit(1);
}
fFileIn = TRUE;
break;
} else {
Usage();
}
*/
default:
fprintf(stderr, "\nInvalid argument: %s\n", argv[argnum]);
Usage();
} //switch
} else {
Usage();
} // if
++argnum;
} // while
} // parseargs
LPSTR pszUsage =
"Generates a listing of each file processed and its check sum.\n\n"
"Usage: %s [/?] display this message\n"
" [/r] recursive file check\n"
" [/p pathname] root path override\n"
" [/o filename] output file name\n"
" [/x name] exclude file or directory\n\n"
"Notes: If no /p path is given, the current directory is processed.\n"
" Exclude multiple files or directories with multiple /x arguments\n"
" e.g. - /x file1 /x file2\n\n"
"Example: %s /r /p c:\\winnt351 /o %s.chk /x symbols /x dump\n"
"";
VOID
Usage()
{
fprintf(stderr, pszUsage, exename, exename, exename);
exit(1);
}
int MyGetFullPathName( IN const CHAR *InPath, IN OUT CHAR *FullPath)
{
int len;
LPSTR FilePart;
len = GetFullPathName(InPath, MAX_PATH, FullPath, &FilePart);
return ( (len>0 && len<MAX_PATH) ? len : 0 );
}