/* xx.xx.94 TedM Created. 10.31.94 JoeHol Added -b switch to help notify of files that have had bug fixes back-out, eg. since media build group uses -d switch, we need to know if a file is now "older" than before so we can also take the "bad newer" file out of the media. Also, verifies src and dst by DOS date and time. 11.01.94 JoeHol -l logs to chk.log, not compress.log. 12.14.94 JoeHol Make -l work with name of log file, use dcomp.log if not specified. 02.07.95 JoeHol -b for bom file. If this is specified we will check that the files are listed in the bom. If a file isn't in the bom, we won't add the file to the list of files to compress. 06.20.95 JoeHol Since we are changing the location of the files, lets just compressed all, ie. disable real use of the -b switch for awhile. 08.03.95 JoeHol If a file isn't found to compress in a directory, we don't stop, but continue gracefully displaying an error message. */ #include #include #include #include #include #include typedef struct _SOURCEFILE { struct _SOURCEFILE *Next; WIN32_FIND_DATA FileData; } SOURCEFILE, *PSOURCEFILE; PSOURCEFILE SourceFileList; DWORD SourceFileCount; PSTR SourceSpec,TargetDirectory; HANDLE NulHandle; CRITICAL_SECTION SourceListCritSect; CRITICAL_SECTION ConsoleCritSect; #define MFL 256 FILE * logFile; FILE * bomFile; char logFileName[MFL]; char bomFileName[MFL]; char infFilePath[MFL]; #define MAX_NUMBER_OF_FILES 5000 // let's assume we only have 5000 files max in the product. #define NAME_SIZE 25 // assume no filename is greater than 25 chars long. char szFiles[MAX_NUMBER_OF_FILES][NAME_SIZE]; BOOL bVerbose = FALSE; BOOL bBomChecking = FALSE; int wRecords=0; DWORD ThreadCount; #define MAX_EXCLUDE_EXTENSION 100 PSTR ExcludeExtension[MAX_EXCLUDE_EXTENSION]; unsigned ExcludeExtensionCount; #define MAX_EXCLUDE_FILEFILTER 100 char ExcludeFileFilter[MAX_EXCLUDE_FILEFILTER][MAX_PATH]; unsigned ExcludeFileFilterCount; HINF ExcludeFilterHandle[MAX_EXCLUDE_FILEFILTER]; PVOID MALLOC( IN DWORD Size ) { return((PVOID)LocalAlloc(LMEM_FIXED,Size)); } VOID FREE( IN PVOID Block ) { LocalFree((HLOCAL)Block); } BOOL AddExtensionToExclude( IN PSTR Extension ) { // // Make sure it starts with a dot. // if((*Extension != '.') || (lstrlen(Extension) < 2)) { return(FALSE); } if(ExcludeExtensionCount < MAX_EXCLUDE_EXTENSION) { ExcludeExtension[ExcludeExtensionCount++] = Extension; } else { printf("Warning: exclude extension %s ignored (%u max).\n",Extension,MAX_EXCLUDE_EXTENSION); } return(TRUE); } BOOL AddFilterToExclude( IN PSTR FilterName ) { DWORD error; if(!FilterName) { return(FALSE); } if(ExcludeFileFilterCount < MAX_EXCLUDE_EXTENSION) { ExcludeFilterHandle[ExcludeFileFilterCount] = SetupOpenInfFile( FilterName, NULL, INF_STYLE_WIN4, &error ); if (ExcludeFilterHandle[ExcludeFileFilterCount] == INVALID_HANDLE_VALUE) { printf("Warning: couldn't open exclude file filter %s (%d).\n", FilterName , error); return(FALSE); } strcpy( ExcludeFileFilter[ExcludeFileFilterCount], FilterName ); ExcludeFileFilterCount += 1; return(TRUE); } else { printf("Warning: exclude file filter %s ignored (%u max).\n",FilterName,MAX_EXCLUDE_EXTENSION); } return(TRUE); } BOOL ParseArguments( IN int argc, IN PSTR *argv ) { HANDLE FindHandle; WIN32_FIND_DATA FindData; while(argc--) { if((**argv == '-') || (**argv == '/')) { switch((*argv)[1]) { case 'b': case 'B': strcpy ( infFilePath, &(*argv)[2] ); printf ( "infFilePath: >>>%s<<<\n", &(*argv)[2] ); bBomChecking = TRUE; break; case 'l': case 'L': strcpy ( logFileName, &(*argv)[2] ); //printf ( ">>>%s<<<\n", &(*argv)[2] ); break; case 'm': case 'M': if(ThreadCount) { return(FALSE); } ThreadCount = (DWORD)atoi((*argv)+2); if(!ThreadCount) { return(FALSE); } break; case 'v': case 'V': bVerbose = TRUE; break; case 'x': case 'X': if((argc-- < 1) || !AddExtensionToExclude(*(++argv))) { return(FALSE); } break; case 'f': case 'F': if((argc-- < 1) || !AddFilterToExclude(*(++argv))) { return(FALSE); } break; default: return(FALSE); } } else { if(SourceSpec) { if(TargetDirectory) { return(FALSE); } else { TargetDirectory = *argv; } } else { SourceSpec = *argv; } } argv++; } if(!TargetDirectory || !SourceSpec) { return(FALSE); } // // Make sure target is a directory. // FindHandle = FindFirstFile(TargetDirectory,&FindData); if(FindHandle == INVALID_HANDLE_VALUE) { return(FALSE); } FindClose(FindHandle); if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { return(FALSE); } return(TRUE); } VOID Usage( VOID ) { printf("DCOMP -llog [-x .ext ...] [-m#] [-d] [-bbom] Source Dest\n\n"); printf(" -x .ext Exclude files with extension .ext.\n"); printf(" -m Force use of # threads.\n"); printf(" -v Verbose.\n" ); printf(" -llog Log filename, dcomp.log default.\n"); printf(" -binfPath your setup\\inf\\win4\\inf path.\n" ); printf(" -f infname Excludes files listed in inf.\n"); printf(" Source Source file specification. Wildcards may be used.\n"); printf(" Destination Specifies directory where renamed compressed files\n"); printf(" will be placed.\n"); } VOID DnpGenerateCompressedName( IN PSTR Filename, OUT PSTR CompressedName ) /*++ Routine Description: Given a filename, generate the compressed form of the name. The compressed form is generated as follows: Look backwards for a dot. If there is no dot, append "._" to the name. If there is a dot followed by 0, 1, or 2 charcaters, append "_". Otherwise assume there is a 3-character extension and replace the third character after the dot with "_". Arguments: Filename - supplies filename whose compressed form is desired. CompressedName - receives compressed file name. Return Value: None. --*/ { PSTR p,q; strcpy(CompressedName,Filename); p = strrchr(CompressedName,'.'); q = strrchr(CompressedName,'\\'); if(q < p) { // // If there are 0, 1, or 2 characters after the dot, just append // the underscore. p points to the dot so include that in the length. // if(lstrlen(p) < 4) { lstrcat(CompressedName,"_"); } else { // // Assume there are 3 characters in the extension. So replace // the final one with an underscore. // p[3] = '_'; } } else { // // No dot, just add ._. // lstrcat(CompressedName,"._"); } } BOOL GetSetTimeStamp( IN PSTR FileName, OUT PFILETIME CreateTime, OUT PFILETIME AccessTime, OUT PFILETIME WriteTime, IN BOOL Set ) { HANDLE h; BOOL b; // // Open the file. // h = CreateFile( FileName, Set ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(h == INVALID_HANDLE_VALUE) { return(FALSE); } b = Set ? SetFileTime(h,CreateTime,AccessTime,WriteTime) : GetFileTime(h,CreateTime,AccessTime,WriteTime); CloseHandle(h); return(b); } VOID mprintf( IN DWORD ThreadSerialNumber, IN PSTR FormatString, ... ) { CHAR msg[2048]; va_list arglist; va_start(arglist,FormatString); vsprintf(msg,FormatString,arglist); vfprintf(logFile,FormatString,arglist); // print to log file va_end(arglist); //EnterCriticalSection(&ConsoleCritSect); if(ThreadCount == 1) { printf(msg); } else { printf("%u: %s",ThreadSerialNumber,msg); } //LeaveCriticalSection(&ConsoleCritSect); } VOID logprintf( IN PSTR FormatString, ... ) { CHAR msg[2048]; va_list arglist; va_start(arglist,FormatString); vsprintf(msg,FormatString,arglist); vfprintf(logFile,FormatString,arglist); // print to log file va_end(arglist); } // SameDosDateTime returns: TRUE if date/times are same; FALSE otherwise. // BOOL SameDosDateTime( IN DWORD ThreadSerialNumber, IN PSTR SourceFileName, IN PSTR DestFileName, IN BOOL bDstMustExistNow ) { HANDLE h; FILETIME ftSourceWriteTime; FILETIME dftDestWriteTime; FILETIME CreateTime, AccessTime; FILETIME dCreateTime, dAccessTime; WORD srcDate, dstDate, srcTime, dstTime; // DOS versions. // // Open the source file. // h = CreateFile( SourceFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(h == INVALID_HANDLE_VALUE) { mprintf ( ThreadSerialNumber, "ERROR CreateFile: %s, gle = %ld\n", SourceFileName, GetLastError() ); return(FALSE); } GetFileTime(h,&CreateTime,&AccessTime,&ftSourceWriteTime); CloseHandle(h); FileTimeToDosDateTime ( &ftSourceWriteTime, &srcDate, &srcTime ); // // Open the destination file. // h = CreateFile( DestFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(h == INVALID_HANDLE_VALUE) { if ( bDstMustExistNow ) { // In case we get something like, FILE_NOT_FOUND // mprintf ( ThreadSerialNumber, "ERROR CreateFile: dst %s, gle = %ld\n", DestFileName,GetLastError()); } return(FALSE); } GetFileTime(h,&dCreateTime,&dAccessTime,&dftDestWriteTime); CloseHandle(h); FileTimeToDosDateTime ( &dftDestWriteTime, &dstDate, &dstTime ); // Compare that the DOS date and times are the same. // if ( (srcDate != dstDate) || (srcTime != dstTime) ) { if ( 1 /*bVerbose*/ ) { mprintf ( ThreadSerialNumber, "Warning, times different: %s = %x-%x, %s = %x-%x\n", SourceFileName, srcDate, srcTime, DestFileName, dstDate, dstTime ); } return (FALSE); } return (TRUE); // DOS dates and times are the same. } DWORD WorkerThread( IN PVOID ThreadParameter ) { PSOURCEFILE SourceFile; DWORD ThreadSerialNumber; CHAR FullSourceName[2*MAX_PATH]; CHAR FullTargetName[2*MAX_PATH]; CHAR CompressedName[2*MAX_PATH]; CHAR CmdLine[5*MAX_PATH]; PCHAR p; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInfo; BOOL b; DWORD d; int i; ThreadSerialNumber = PtrToUlong(ThreadParameter); ZeroMemory(&StartupInfo,sizeof(StartupInfo)); StartupInfo.cb = sizeof(STARTUPINFO); while(1) { // // Pluck the next source file off the list. // If there is no next source file, we're done. // EnterCriticalSection(&SourceListCritSect); SourceFile = SourceFileList; if(!SourceFile) { LeaveCriticalSection(&SourceListCritSect); return(TRUE); } SourceFileList = SourceFile->Next; LeaveCriticalSection(&SourceListCritSect); // // Form the source file name. // lstrcpy(FullSourceName,SourceSpec); if(p = strrchr(FullSourceName,'\\')) { p++; } else { p = FullSourceName; } lstrcpy(p,SourceFile->FileData.cFileName); // // Form full target name. // lstrcpy(FullTargetName,TargetDirectory); i = lstrlen(FullTargetName); if(FullTargetName[i-1] != '\\') { FullTargetName[i] = '\\'; FullTargetName[i+1] = 0; } lstrcat(FullTargetName,SourceFile->FileData.cFileName); DnpGenerateCompressedName(FullTargetName,CompressedName); // // If the update flag is set, check timestamps. // If this fails, assume the target file does not exist. // if ( !SameDosDateTime ( ThreadSerialNumber, FullSourceName, CompressedName, FALSE ) ) { // // Form the command line for the child process. // sprintf(CmdLine,"diamond /D CompressionType=LZX /D CompressionMemory=21 %s %s",FullSourceName,CompressedName); if ( 1 /*bVerbose*/ ) { mprintf(ThreadSerialNumber, "Start %s ==> %s\n",FullSourceName,CompressedName); } // // Invoke the child process. // b = CreateProcess( NULL, CmdLine, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &StartupInfo, &ProcessInfo ); if(b) { CloseHandle(ProcessInfo.hThread); // // Wait for the child process to terminate and get its // return code. // WaitForSingleObject(ProcessInfo.hProcess,INFINITE); b = GetExitCodeProcess(ProcessInfo.hProcess,&d); CloseHandle(ProcessInfo.hProcess); if(b) { // // 'd' value of 0 means success, we've compressed the // file. Now, set the time stamp on the target file. // if(d) { // 'd' not 0, error occurred. // mprintf(ThreadSerialNumber,"ERROR compressing %s to %s.\n",FullSourceName,CompressedName); } else { // 'd' is 0, we compressed the file, now time stamp. // // First get the source time's stamp. // GetSetTimeStamp( FullSourceName, &SourceFile->FileData.ftCreationTime, &SourceFile->FileData.ftLastAccessTime, &SourceFile->FileData.ftLastWriteTime, FALSE ); // Now, set the destination time's stamp. // GetSetTimeStamp( CompressedName, &SourceFile->FileData.ftCreationTime, &SourceFile->FileData.ftLastAccessTime, &SourceFile->FileData.ftLastWriteTime, TRUE ); // Double check it is really set. // if ( SameDosDateTime ( ThreadSerialNumber, FullSourceName, CompressedName, TRUE ) ) { if ( bVerbose ) { mprintf(ThreadSerialNumber,"Compressed %s to %s.\n",FullSourceName,CompressedName); } } else { mprintf(ThreadSerialNumber,"ERROR Compressed %s to %s - time stamps don't match.\n",FullSourceName,CompressedName); } } } else { mprintf(ThreadSerialNumber,"ERROR Unable to get process \"%s\" termination code.\n",CmdLine); } } else { mprintf(ThreadSerialNumber,"ERROR Unable to invoke \"%s\".\n",CmdLine); } } else { if ( bVerbose ) { mprintf(ThreadSerialNumber, "%s is up to date.\n",CompressedName); } } } } // FileInBom // Returns TRUE if fileName is located in the BOM; FALSE otherwise. // BOOL FileInBom ( char * fileName ) { int i; for ( i = 0; i < wRecords; ++i ) { if ( _stricmp(fileName, szFiles[i] ) == 0 ) { return(TRUE); } } return(FALSE); } // AddSourceFile // returns FALSE if an error. // returns TRUE if no error occrred, but not necesarrily meaning a file // was added. BOOL AddSourceFile( IN PWIN32_FIND_DATA FindData ) { static PSOURCEFILE LastFile = NULL; PSOURCEFILE SourceFile; unsigned i; DWORD ExtensionLength; DWORD FileNameLength; INFCONTEXT InfContext; // If we are doing BOM checking, make sure that this file is // listed in the BOM. If it is, continue on to the next checking. // If it is NOT, just return. // if ( bBomChecking && !FileInBom(FindData->cFileName) ) { return(TRUE); } // // Make sure this file is not of a type that is // supposed to be excluded from the list of files // we care about. // FileNameLength = lstrlen(FindData->cFileName); for(i=0; i ExtensionLength) && !lstrcmpi(ExcludeExtension[i],FindData->cFileName+FileNameLength-ExtensionLength)) { return(TRUE); } } for(i=0; icFileName, &InfContext )) { return(TRUE); } } SourceFile = MALLOC(sizeof(SOURCEFILE)); if(!SourceFile) { printf("ERROR Insufficient memory.\n"); return(FALSE); } SourceFile->FileData = *FindData; SourceFile->Next = NULL; if(LastFile) { LastFile->Next = SourceFile; } else { SourceFileList = SourceFile; } LastFile = SourceFile; SourceFileCount++; return(TRUE); } BOOL FindSourceFiles( VOID ) { WIN32_FIND_DATA FindData; HANDLE FindHandle; printf("Building list of source files...\n"); // // Build a list of source files. // FindHandle = FindFirstFile(SourceSpec,&FindData); if(FindHandle != INVALID_HANDLE_VALUE) { if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if(!AddSourceFile(&FindData)) { FindClose(FindHandle); return(FALSE); } } while(FindNextFile(FindHandle,&FindData)) { if(!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if(!AddSourceFile(&FindData)) { FindClose(FindHandle); return(FALSE); } } } FindClose(FindHandle); } if(!SourceFileCount) { printf("Warning: No files found in: %s.\n",SourceSpec); } return(SourceFileCount != 0); } VOID DoIt( VOID ) { SYSTEM_INFO SystemInfo; PHANDLE ThreadHandles; DWORD i; DWORD ThreadId; // // Determine number of threads to use. // This is based on the number of processors. // if(!ThreadCount) { GetSystemInfo(&SystemInfo); ThreadCount = SystemInfo.dwNumberOfProcessors; } ThreadHandles = MALLOC(ThreadCount * sizeof(HANDLE)); if(!ThreadHandles) { printf("ERROR Insufficient memory.\n"); exit(1); } if ( bVerbose ) { if(ThreadCount == 1) { printf("Compressing %u files...\n",SourceFileCount); } else { printf("Compressing %u files (%u threads)...\n",SourceFileCount,ThreadCount); } } // // Initialize the source file list critical section. // InitializeCriticalSection(&SourceListCritSect); // // Create threads. // for(i=0; i