/**************************************************************************************\ * 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 #include #include #include #include #include #include #include #include #include // 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; iName, 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; iAttributes & 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; iSize != 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