/************************************************************************ * Compdir: compare directories * ************************************************************************/ #define IF_GET_ATTR_FAILS( FileName, Attributes) GET_ATTRIBUTES( FileName, Attributes); if ( Attributes == GetFileAttributeError) #define FIND_FIRST( String, Buff) FindFirstFile( String, &Buff) #define FIND_NEXT( handle, Buff) !FindNextFile( handle, &Buff) #define FREE( memory) #define MYSTRCAT( FirstString, SecondString) strcat( strcpy( _alloca( strlen( FirstString) + strlen( SecondString) + 1), FirstString), SecondString) #define COMBINETHREESTRINGS( FirstString, SecondString, ThirdString) strcat( strcat( strcpy( _alloca( strlen( FirstString) + strlen( SecondString) + strlen( ThirdString) + 1), FirstString), SecondString), ThirdString) #include "compdir.h" #include "imagehlp.h" int Mymemcmp( const void *buf1, const void *buf2, size_t count ); char RebasedFile[MAX_PATH]; // used in BinaryCompares with /i switch char *RebasedFile2 = NULL; // used in BinaryCompares with /i switch LinkedFileList MatchList = NULL; LinkedFileList MStarList = NULL; LinkedFileList ExcludeList = NULL; LinkedFileList EStarList = NULL; DWORD Granularity = 0; // used in ParseArgs ATTRIBUTE_TYPE CompareAttribute, NegativeCompareAttribute; // used as file compare criteria BOOL CompareAttributeSet = FALSE; BOOL NegativeCompareAttributeSet = FALSE; BOOL Excludes = FALSE; BOOL Matches = FALSE; BOOL RunningOnNT = FALSE; BOOL DealingWithDirectories; fSpecAttribs = FALSE; fBreakLinks = FALSE; fCheckAttribs = FALSE; fCheckBits = FALSE; fChecking = FALSE; fCheckSize = FALSE; fCheckTime = FALSE; fCreateNew = FALSE; fCreateLink = FALSE; fDoNotDelete = FALSE; fDoNotRecurse = FALSE; fDontCopyAttribs = FALSE; fDontLowerCase = FALSE; fExclude = FALSE; fExecute = FALSE; fForce = FALSE; fIgnoreRs = FALSE; fIgnoreSlmFiles = FALSE; fMatching = FALSE; fMultiThread = FALSE; fOnlyIfExists = FALSE; fOpposite = FALSE; fScript = FALSE; fTrySis = FALSE; fVerbose = FALSE; void __cdecl main( int argc, char **argv) { ATTRIBUTE_TYPE Attributes1, Attributes2; char *Path1, *Path2; OSVERSIONINFO VersionInformation; SYSTEM_INFO SystemInformation; ExitValue = 0; Attributes1 = GetFileAttributeError; Attributes2 = GetFileAttributeError; ProcessModeDefault = TRUE; // Used by opposite mode ParseEnvArgs( ); // Parse COMPDIRCMD environment variable ParseArgs( argc, argv); // Check argument validity. // // Check existence of first path. // IF_GET_ATTR_FAILS( argv[argc - 2], Attributes1) { fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 2], GetLastError()); exit( 1); } IF_GET_ATTR_FAILS( argv[argc - 1], Attributes2) { if ( !fCreateNew) { fprintf( stderr, "Could not find %s (error = %d)\n", argv[argc - 1], GetLastError()); exit( 1); } } // // If second directory is a drive letter append path of first directory // to it // if ( ( strlen( argv[argc-1]) == 2) && ( *( argv[argc-1] + 1) == ':') ) { if ( ( Path2 = _fullpath( NULL, argv[argc-2], 0)) == NULL) { Path2 = argv[argc-1]; } else { Path2[0] = *( argv[argc-1]); IF_GET_ATTR_FAILS( Path2, Attributes2) { if ( !fCreateNew) { fprintf( stderr, "Could not find %s (error = %d)\n", Path2, GetLastError()); exit( 1); } } } } else if ( ( Path2 = _fullpath( NULL, argv[argc-1], 0)) == NULL) { Path2 = argv[argc-1]; } if ( ( Path1 = _fullpath( NULL, argv[argc-2], 0)) == NULL) { Path1 = argv[argc-2]; } if ( !fDontLowerCase) { _strlwr( Path1); _strlwr( Path2); } if ( fVerbose) { fprintf( stdout, "Compare criterion: existence" ); if ( fCheckSize) { fprintf( stdout, ", size" ); } if ( fCheckTime) { fprintf( stdout, ", date/time" ); } if ( fCheckBits) { fprintf( stdout, ", contents" ); } fprintf( stdout, "\n" ); fprintf( stdout, "Path1: %s\n", Path1); fprintf( stdout, "Path2: %s\n", Path2); } VersionInformation.dwOSVersionInfoSize = sizeof( OSVERSIONINFO); if ( GetVersionEx( &VersionInformation) ) { if ( VersionInformation.dwPlatformId == VER_PLATFORM_WIN32_NT ) { RunningOnNT = TRUE; } } if ( ( fCreateLink) || ( fBreakLinks) || ( fTrySis)) { if ( RunningOnNT) { NtDll = LoadLibrary( "ntdll.dll"); if ( !NtDll) { fprintf( stderr, "Could not find ntdll.dll. Can't perform /l or /$\n"); fCreateLink = FALSE; fTrySis = FALSE; ExitValue = 1; } else { if ( !InitializeNtDllFunctions()) { fprintf( stderr, "Could not load ntdll.dll. Can't perform /l or /$\n"); fCreateLink = FALSE; fTrySis = FALSE; ExitValue = 1; } } } else { fprintf( stderr, "/l and /$ only work on NT. Can't perform /l or /$\n"); fCreateLink = FALSE; fTrySis = FALSE; ExitValue = 1; } } if ( fMultiThread) { // // Query the number of processors from the system and // default the number of worker threads to 4 times that. // GetSystemInfo( &SystemInformation ); NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4; if ( fVerbose) { fprintf( stdout, "Processors: %d\n", SystemInformation.dwNumberOfProcessors ); } // // Allocate a thread local storage slot for use by our worker // thread routine ( ProcessRequest). This call reserves a // 32-bit slot in the thread local storage array for every // thread in this process. Remember the slot index in a global // variable for use by our worker thread routine. // TlsIndex = TlsAlloc(); if ( TlsIndex == 0xFFFFFFFF) { fprintf( stderr, "Unable to allocate thread local storage.\n" ); fMultiThread = FALSE; ExitValue = 1; } // // Create a work queue, which will create the specified number of threads // to process. // CDWorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest ); if ( CDWorkQueue == NULL) { fprintf( stderr, "Unable to create %u worker threads.\n", NumberOfWorkerThreads ); fMultiThread = FALSE; ExitValue = 1; } // // Mutual exclusion between and requests that are creating paths // is done with a critical section. // InitializeCriticalSection( &CreatePathCriticalSection ); } if ( Attributes1 & FILE_ATTRIBUTE_DIRECTORY) { DealingWithDirectories = TRUE; } else { DealingWithDirectories = FALSE; } if ( Matches) { SparseTree = TRUE; } else { SparseTree = FALSE; } if ( fCreateNew) { IF_GET_ATTR_FAILS( Path2, Attributes2) { fprintf ( stdout, "Making %s\t", Path2); if ( !MyCreatePath( Path2, DealingWithDirectories)) { fprintf ( stderr, "Unable to create path %s\n", Path2); fprintf ( stdout, "\n"); ExitValue = 1; } else { fprintf( stdout, "[OK]\n"); CompDir( Path1, Path2); } } else { CompDir( Path1, Path2); } } else { CompDir( Path1, Path2); } free( Path1); free( Path2); if ( fIgnoreRs) { _unlink( RebasedFile2); // Delete RebasedFile that might have been created } if ( fMultiThread) { // // This will wait for the work queues to empty before terminating the // worker threads and destroying the queue. // DestroyWorkQueue( CDWorkQueue ); DeleteCriticalSection( &CreatePathCriticalSection ); } if ( fCreateLink) { FreeLibrary( NtDll); } exit( ExitValue); } // main BOOL NoMapBinaryCompare ( char *file1, char *file2) { register int char1, char2; FILE *filehandle1, *filehandle2; if ( ( filehandle1 = fopen ( file1, "rb")) == NULL) { fprintf ( stderr, "cannot open %s\n", file1); ExitValue = 1; return ( FALSE); } if ( ( filehandle2 = fopen( file2, "rb")) == NULL) { fprintf( stderr, "cannot open %s\n", file2); fclose( filehandle1); ExitValue = 1; return( FALSE); } while ( TRUE) { if ( ( char1 = getc( filehandle1)) != EOF) { if ( ( char2 = getc( filehandle2)) != EOF) { if ( char1 != char2) { fclose( filehandle1); fclose( filehandle2); return( FALSE); } } else { fclose( filehandle1); fclose( filehandle2); return( FALSE); } } else { if ( ( char2 = getc( filehandle2)) == EOF) { fclose( filehandle1); fclose( filehandle2); return( TRUE); } else { fclose( filehandle1); fclose( filehandle2); return( FALSE); } } } } BOOL BinaryCompare( char *file1, char *file2) { HANDLE hFile1, hFile2; HANDLE hMappedFile1, hMappedFile2; BOOL IsNTImage = FALSE; LPVOID MappedAddr1, MappedAddr2; PIMAGE_NT_HEADERS32 NtHeader1, NtHeader2; ULONG OldImageSize, NewImageSize; ULONG_PTR OldImageBase, NewImageBase; // fprintf( stdout, "file1: %s, file2: %s\n", file1, file2); // // File1 Mapping // if ( ( hFile1 = CreateFile( file1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )) == (HANDLE)-1) { fprintf( stderr, "Unable to open %s, error code %d\n", file1, GetLastError() ); if ( hFile1 != INVALID_HANDLE_VALUE) { CloseHandle( hFile1 ); } return FALSE; } hMappedFile1 = CreateFileMapping( hFile1, NULL, PAGE_WRITECOPY, 0, 0, NULL ); if ( hMappedFile1 == NULL) { fprintf( stderr, "Unable to map %s, error code %d\n", file1, GetLastError() ); CloseHandle( hFile1); return FALSE; } MappedAddr1 = MapViewOfFile( hMappedFile1, FILE_MAP_COPY, 0, 0, 0 ); if ( MappedAddr1 == NULL) { fprintf( stderr, "Unable to get mapped view of %s, error code %d\n", file1, GetLastError() ); CloseHandle( hFile1 ); return FALSE; } CloseHandle( hMappedFile1); // // File2 rebasing and mapping // if ( fIgnoreRs) { if ( ( ( PIMAGE_DOS_HEADER)MappedAddr1)->e_magic == IMAGE_DOS_SIGNATURE) { try { NtHeader1 = ( PIMAGE_NT_HEADERS32)( (PCHAR)MappedAddr1 + ( (PIMAGE_DOS_HEADER)MappedAddr1)->e_lfanew); if ( NtHeader1->Signature == IMAGE_NT_SIGNATURE) { NewImageBase = ( NtHeader1->OptionalHeader.ImageBase); if ( ( RebasedFile2 != NULL) && ( CopyFile ( file2, RebasedFile2, FALSE)) && ( ReBaseImage( RebasedFile2, NULL, TRUE, FALSE, FALSE, 0, &OldImageSize, &OldImageBase, &NewImageSize, &NewImageBase, 0 )) ) { IsNTImage = TRUE; } } } except( EXCEPTION_EXECUTE_HANDLER ) {} } } if ( IsNTImage) { if ( ( hFile2 = CreateFile( RebasedFile2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )) == (HANDLE)-1) { fprintf( stderr, "Unable to open %s, error code %d\n", RebasedFile2, GetLastError() ); if ( hFile2 != INVALID_HANDLE_VALUE) { CloseHandle( hFile2 ); } return FALSE; } } else { if ( ( hFile2 = CreateFile( file2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL )) == (HANDLE)-1 ) { fprintf( stderr, "Unable to open %s, error code %d\n", file2, GetLastError() ); if ( hFile2 != INVALID_HANDLE_VALUE) { CloseHandle( hFile2 ); } return FALSE; } } hMappedFile2 = CreateFileMapping( hFile2, NULL, PAGE_WRITECOPY, 0, 0, NULL ); if ( hMappedFile2 == NULL) { fprintf( stderr, "Unable to map %s, error code %d\n", file2, GetLastError() ); CloseHandle( hFile2); return FALSE; } MappedAddr2 = MapViewOfFile( hMappedFile2, FILE_MAP_COPY, 0, 0, 0 ); if ( MappedAddr2 == NULL) { fprintf( stderr, "Unable to get mapped view of %s, error code %d\n", file1, GetLastError() ); UnmapViewOfFile( MappedAddr1 ); CloseHandle( hFile1 ); return FALSE; } CloseHandle( hMappedFile2); if ( fIgnoreRs & IsNTImage) { if ( ( (PIMAGE_DOS_HEADER)MappedAddr2)->e_magic == IMAGE_DOS_SIGNATURE) { try { NtHeader2 = (PIMAGE_NT_HEADERS32)( (PCHAR)MappedAddr2 + ( (PIMAGE_DOS_HEADER)MappedAddr2)->e_lfanew); if ( NtHeader2->Signature == IMAGE_NT_SIGNATURE) { IsNTImage = IsNTImage & TRUE; } } except( EXCEPTION_EXECUTE_HANDLER ) {} } } // // Main compare block // if ( fIgnoreRs) { if ( IsNTImage) { try { ULONG i, c; ULONG DirectoryAddressA; ULONG DirectoryAddressB; ULONG DirectoryAddressD; ULONG DirectoryAddressE; ULONG DirectoryAddressI; ULONG DirectoryAddressR; ULONG SizetoEndofFile1 = 0; ULONG SizetoResource1 = 0; ULONG SizeZeroedOut1 = 0; ULONG SizetoEndofFile2 = 0; ULONG SizetoResource2 = 0; ULONG SizeZeroedOut2 = 0; PIMAGE_SECTION_HEADER NtSection; PIMAGE_DEBUG_DIRECTORY Debug; PIMAGE_EXPORT_DIRECTORY Export; BOOL DeleteHeader, AfterResource; // // Set up virtual addresses of sections of interest // DirectoryAddressA = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress; DirectoryAddressB = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress; DirectoryAddressD = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; DirectoryAddressI = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; DirectoryAddressE = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; DirectoryAddressR = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; // // Zero out Binding Info // if ( ( DirectoryAddressB < NtHeader1->OptionalHeader.SizeOfHeaders) && ( 0 < DirectoryAddressB)) { // fprintf( stdout, "ZeroMemoryBa %lx\n", DirectoryAddressB ); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr1 + DirectoryAddressB) , NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size); } NtSection = (PIMAGE_SECTION_HEADER)( (ULONG_PTR)NtHeader1 + sizeof( ULONG) + sizeof( IMAGE_FILE_HEADER) + NtHeader1->FileHeader.SizeOfOptionalHeader ); AfterResource = FALSE; // Initialize // // Loop through file1 mapping zeroing out ignore sections // for ( i=0; iFileHeader.NumberOfSections; i++) { DeleteHeader = FALSE; // Initialize // // Deal with IAT // if ( DirectoryAddressA >= NtSection->VirtualAddress && DirectoryAddressA < NtSection->VirtualAddress + NtSection->SizeOfRawData) { // fprintf ( stdout, "ZeroMemoryA1 start %lx and length %lx\n", ( ( DirectoryAddressA - NtSection->VirtualAddress) + NtSection->PointerToRawData), // NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr1 + ( DirectoryAddressA - NtSection->VirtualAddress) + NtSection->PointerToRawData), NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size); DeleteHeader = TRUE; } // // Deal with Import // if ( DirectoryAddressI >= NtSection->VirtualAddress && DirectoryAddressI < NtSection->VirtualAddress + NtSection->SizeOfRawData) { // fprintf ( stdout, "ZeroMemoryI1 start %lx and length %lx\n", ( ( DirectoryAddressI - NtSection->VirtualAddress) + NtSection->PointerToRawData), // NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr1 + ( DirectoryAddressI - NtSection->VirtualAddress) + NtSection->PointerToRawData), NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size); DeleteHeader = TRUE; } // // Deal with Export // if ( DirectoryAddressE >= NtSection->VirtualAddress && DirectoryAddressE < NtSection->VirtualAddress + NtSection->SizeOfRawData) { ULONG NumberOfExportDirectories; NumberOfExportDirectories = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size / sizeof( IMAGE_EXPORT_DIRECTORY ); Export = (PIMAGE_EXPORT_DIRECTORY)( (ULONG_PTR)MappedAddr1 + ( DirectoryAddressE - NtSection->VirtualAddress) + NtSection->PointerToRawData); for ( c=0; cVirtualAddress) + NtSection->PointerToRawData), // NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size); Export->TimeDateStamp = 0; Export++; } } // // Deal with Debug // if ( DirectoryAddressD >= NtSection->VirtualAddress && DirectoryAddressD < NtSection->VirtualAddress + NtSection->SizeOfRawData) { DWORD TimeDate; ULONG NumberOfDebugDirectories; NumberOfDebugDirectories = NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof( IMAGE_DEBUG_DIRECTORY ); Debug = (PIMAGE_DEBUG_DIRECTORY)( (ULONG_PTR)MappedAddr1 + ( DirectoryAddressD - NtSection->VirtualAddress) + NtSection->PointerToRawData); for ( c=0; cVirtualAddress) + NtSection->PointerToRawData), // NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size); if (Debug->PointerToRawData && Debug->SizeOfData) { ZeroMemory((PVOID)((ULONG_PTR)MappedAddr1 + Debug->PointerToRawData), Debug->SizeOfData); } Debug->PointerToRawData = 0; if (c == 0) { TimeDate = Debug->TimeDateStamp; } Debug->TimeDateStamp = 0; Debug++; } while ( Debug->TimeDateStamp == TimeDate) { Debug->TimeDateStamp = 0; Debug++; } } // // Deal with Resource // if ( DirectoryAddressR >= NtSection->VirtualAddress && DirectoryAddressR < NtSection->VirtualAddress + NtSection->SizeOfRawData) { SizetoResource1 = ( ( DirectoryAddressR - NtSection->VirtualAddress) + NtSection->PointerToRawData); SizeZeroedOut1 = NtSection->SizeOfRawData; // fprintf ( stdout, "ZeroMemoryR1 start %lx and length %lx\n", SizetoResource1, // SizeZeroedOut1); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr1 + SizetoResource1), SizeZeroedOut1); DeleteHeader = TRUE; AfterResource = TRUE; } // // Deal with Header // if ( DeleteHeader || AfterResource) { // fprintf ( stdout, "ZeroMemoryH1 start %lx and length %lx\n", (PUCHAR)NtSection - (PUCHAR)MappedAddr1, sizeof( IMAGE_SECTION_HEADER)); ZeroMemory( NtSection, sizeof( IMAGE_SECTION_HEADER)); } ++NtSection; } // // Set up virtual addresses of sections of interest // DirectoryAddressA = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress; DirectoryAddressB = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress; DirectoryAddressI = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; DirectoryAddressE = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; DirectoryAddressD = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; DirectoryAddressR = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; NtSection = (PIMAGE_SECTION_HEADER)( (ULONG_PTR)NtHeader2 + sizeof( ULONG) + sizeof( IMAGE_FILE_HEADER) + NtHeader2->FileHeader.SizeOfOptionalHeader ); // // Zero out Binding Info // if ( ( DirectoryAddressB < NtHeader2->OptionalHeader.SizeOfHeaders) && ( 0 < DirectoryAddressB)) { // fprintf( stdout, "ZeroMemoryBb %lx\n", DirectoryAddressB ); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr2 + DirectoryAddressB) , NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size); } AfterResource = FALSE; //Initialize // // Loop through file2 mapping zeroing out ignore sections // for ( i=0; iFileHeader.NumberOfSections; i++) { DeleteHeader = FALSE; // Initialize // // Deal with IAT // if ( DirectoryAddressA >= NtSection->VirtualAddress && DirectoryAddressA < NtSection->VirtualAddress + NtSection->SizeOfRawData) { // fprintf ( stdout, "ZeroMemoryA2 start %lx and length %lx\n", ( ( DirectoryAddressA - NtSection->VirtualAddress) + NtSection->PointerToRawData), // NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr2 + ( DirectoryAddressA - NtSection->VirtualAddress) + NtSection->PointerToRawData), NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size); DeleteHeader = TRUE; } // // Deal with Import // if ( DirectoryAddressI >= NtSection->VirtualAddress && DirectoryAddressI < NtSection->VirtualAddress + NtSection->SizeOfRawData) { // fprintf ( stdout, "ZeroMemoryI2 start %lx and length %lx\n", ( ( DirectoryAddressI - NtSection->VirtualAddress) + NtSection->PointerToRawData), // NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr2 + ( DirectoryAddressI - NtSection->VirtualAddress) + NtSection->PointerToRawData), NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size); DeleteHeader = TRUE; } // // Deal with Export // if ( DirectoryAddressE >= NtSection->VirtualAddress && DirectoryAddressE < NtSection->VirtualAddress + NtSection->SizeOfRawData) { ULONG NumberOfExportDirectories; NumberOfExportDirectories = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size / sizeof( IMAGE_EXPORT_DIRECTORY ); Export = (PIMAGE_EXPORT_DIRECTORY)( (ULONG_PTR)MappedAddr2 + ( DirectoryAddressE - NtSection->VirtualAddress) + NtSection->PointerToRawData); for ( c=0; cVirtualAddress) + NtSection->PointerToRawData), // NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size); Export->TimeDateStamp = 0; Export++; } } // // Deal with Debug // if ( DirectoryAddressD >= NtSection->VirtualAddress && DirectoryAddressD < NtSection->VirtualAddress + NtSection->SizeOfRawData) { DWORD TimeDate; ULONG NumberOfDebugDirectories; NumberOfDebugDirectories = NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof( IMAGE_DEBUG_DIRECTORY ); Debug = (PIMAGE_DEBUG_DIRECTORY)( (ULONG_PTR)MappedAddr2 + ( DirectoryAddressD - NtSection->VirtualAddress) + NtSection->PointerToRawData); for ( c=0; cVirtualAddress) + NtSection->PointerToRawData), // NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size); if (Debug->PointerToRawData && Debug->SizeOfData) { ZeroMemory((PVOID)((ULONG_PTR)MappedAddr2 + Debug->PointerToRawData), Debug->SizeOfData); } Debug->PointerToRawData = 0; if (c == 0) { TimeDate = Debug->TimeDateStamp; } Debug->TimeDateStamp = 0; Debug++; } while ( Debug->TimeDateStamp == TimeDate) { Debug->TimeDateStamp = 0; Debug++; } } // // Deal with Resource // if ( DirectoryAddressR >= NtSection->VirtualAddress && DirectoryAddressR < NtSection->VirtualAddress + NtSection->SizeOfRawData) { SizetoResource2 = ( ( DirectoryAddressR - NtSection->VirtualAddress) + NtSection->PointerToRawData); SizeZeroedOut2 = NtSection->SizeOfRawData; // fprintf ( stdout, "ZeroMemoryR2 start %lx and length %lx\n", SizetoResource2, // SizeZeroedOut2); ZeroMemory( (PVOID)( (ULONG_PTR)MappedAddr2 + SizetoResource2), SizeZeroedOut2); DeleteHeader = TRUE; AfterResource = TRUE; } // // Deal with Header // if ( DeleteHeader || AfterResource) { // fprintf( stdout, "ZeroMemoryH2 start %lx and length %lx\n", (PUCHAR)NtSection - (PUCHAR)MappedAddr2, sizeof( IMAGE_SECTION_HEADER)); ZeroMemory( NtSection, sizeof( IMAGE_SECTION_HEADER)); } ++NtSection; } // // Zero out header info // NtHeader1->FileHeader.TimeDateStamp = 0; NtHeader2->FileHeader.TimeDateStamp = 0; NtHeader1->OptionalHeader.CheckSum = 0; NtHeader2->OptionalHeader.CheckSum = 0; NtHeader1->OptionalHeader.SizeOfInitializedData = 0; NtHeader2->OptionalHeader.SizeOfInitializedData = 0; NtHeader1->OptionalHeader.SizeOfImage = 0; NtHeader2->OptionalHeader.SizeOfImage = 0; NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0; NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0; NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; NtHeader1->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; NtHeader2->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; // // Do compares here // if ( SizetoResource1 == SizetoResource2) { if ( memcmp( MappedAddr1, MappedAddr2, SizetoResource1) == 0) { SizetoEndofFile1 = GetFileSize( hFile1, NULL) - ( SizetoResource1 + SizeZeroedOut1); SizetoEndofFile2 = GetFileSize( hFile2, NULL) - ( SizetoResource2 + SizeZeroedOut2); if ( SizetoEndofFile1 == SizetoEndofFile2) { if ( memcmp( (PVOID)( (ULONG_PTR)MappedAddr1 + SizetoResource1 + SizeZeroedOut1), (PVOID)( ( ULONG_PTR)MappedAddr2 + SizetoResource2 + SizeZeroedOut2), SizetoEndofFile1) == 0) { UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); return TRUE; } } } } UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); return FALSE; } except( EXCEPTION_EXECUTE_HANDLER ) { UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); if ( !NoMapBinaryCompare( file1, file2)) { return FALSE; } else { return TRUE; } } } } if ( GetFileSize( hFile1, NULL) == GetFileSize( hFile2, NULL) ) { try { if ( memcmp( MappedAddr1, MappedAddr2, GetFileSize( hFile1, NULL)) == 0) { UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); return TRUE; } } except( EXCEPTION_EXECUTE_HANDLER ) { UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); if ( !NoMapBinaryCompare( file1, file2)) { return FALSE; } else { return TRUE; } } } UnmapViewOfFile( MappedAddr1 ); UnmapViewOfFile( MappedAddr2 ); CloseHandle( hFile1 ); CloseHandle( hFile2 ); return FALSE; } int Mymemcmp( const void *buf1, const void *buf2, size_t count ) { size_t memoffset = 0; int retval = FALSE; do { try { if ( memcmp( (PVOID)( (PCHAR)buf1 + memoffset), (PVOID)( (PCHAR)buf2 + memoffset), sizeof( size_t)) != 0) { fprintf( stdout, "Offset is %Lx ", memoffset); fprintf( stdout, "Contents are %Lx and %Lx\n", *( (PULONG)( (PCHAR)buf1 + memoffset)), *( (PULONG)( (PCHAR)buf2 + memoffset)) ); retval = TRUE; } } except( EXCEPTION_EXECUTE_HANDLER ) { fprintf( stdout, "Memory not allocated\n"); } } while ( ( memoffset = memoffset + sizeof( size_t)) < count); return retval; } // // CompDir turns Path1 and Path2 into: // // AddList - Files that exist in Path1 but not in Path2 // // DelList - Files that do not exist in Path1 but exist in Path2 // // DifList - Files that are different between Path1 and Path2 based // on criteria provided by flags passed to CompDir // // It then passes these lists to CompLists and processes the result. // void CompDir( char *Path1, char *Path2) { LinkedFileList AddList, DelList, DifList; struct CFLStruct Parameter1, Parameter2; DWORD Id; HANDLE Threads[2]; DWORD CFReturn; AddList = NULL; // DelList = NULL; // Start with empty lists DifList = NULL; // Parameter1.List = &AddList; Parameter1.Path = Path1; if ( fMultiThread) { Threads[0] = CreateThread( NULL, 0, CreateFileList, &Parameter1, 0, &Id ); if ( Threads[0] == NULL) { fprintf( stderr, "CreateThread1Failed, error code %d\n", GetLastError() ); CreateFileList( &Parameter1); fMultiThread = FALSE; } } else { CreateFileList( &Parameter1); } Parameter2.List = &DelList; Parameter2.Path = Path2; if ( fMultiThread) { Threads[1] = CreateThread( NULL, 0, CreateFileList, &Parameter2, 0, &Id ); if ( Threads[1] == NULL) { fprintf( stderr, "CreateThread2Failed, error code %d\n", GetLastError() ); CFReturn = CreateFileList( &Parameter2); fMultiThread = FALSE; } } else { CFReturn = CreateFileList( &Parameter2); } if ( fMultiThread) { Id = WaitForMultipleObjects( 2, Threads, TRUE, (DWORD)-1 ); GetExitCodeThread( Threads[1], &CFReturn); CloseHandle( Threads[0]); CloseHandle( Threads[1]); } if ( CFReturn == 0) { CompLists( &AddList, &DelList, &DifList, Path1, Path2); ProcessLists( AddList, DelList, DifList, Path1, Path2); } FreeList( &DifList); FreeList( &DelList); FreeList( &AddList); } // CompDir BOOL FilesDiffer( LinkedFileList File1, LinkedFileList File2, char *Path1, char *Path2) { DWORD High1, High2, Low1, Low2; // Used in comparing times BOOL Differ = FALSE; char *FullPath1, *FullPath2; // // Check if same name is a directory under Path1 // and a file under Path2 or vice-versa // if ( ( (*File1).Attributes & FILE_ATTRIBUTE_DIRECTORY) || ( (*File2).Attributes & FILE_ATTRIBUTE_DIRECTORY) ) { if ( ( (*File1).Attributes & FILE_ATTRIBUTE_DIRECTORY) && ( (*File2).Attributes & FILE_ATTRIBUTE_DIRECTORY)) { if ( !fDoNotRecurse) { // // Create Full Path Strings // _strrev( (*File1).Name); _strrev( (*File2).Name); if ( DealingWithDirectories) { ( Path1[strlen( Path1) - 1] == '\\') ? ( FullPath1 = MYSTRCAT( Path1, (*File1).Name)) : ( FullPath1 = COMBINETHREESTRINGS( Path1, "\\", (*File1).Name)); ( Path2[strlen( Path2) - 1] == '\\') ? ( FullPath2 = MYSTRCAT( Path2, (*File1).Name)) : ( FullPath2 = COMBINETHREESTRINGS( Path2, "\\", (*File1).Name)); } else { FullPath1 = MYSTRCAT( Path1, ""); FullPath2 = MYSTRCAT( Path2, ""); } _strrev( (*File1).Name); _strrev( (*File2).Name); CompDir( FullPath1, FullPath2); FREE( FullPath1); FREE( FullPath2); } } else { if( ! ( (*File1).Attributes & FILE_ATTRIBUTE_DIRECTORY)) { strcat( (*File1).Flag, "@"); } else { strcat( (*File2).Flag, "@"); } Differ = TRUE; } } else { if ( fCheckTime) { if ( Granularity) { // // Bit manipulation to deal with large integers. // High1 = (*File1).Time.dwHighDateTime>>23; High2 = (*File2).Time.dwHighDateTime>>23; if ( High1 == High2) { Low1 = ( (*File1).Time.dwHighDateTime<<9) | ( (*File1).Time.dwLowDateTime>>23); Low2 = ( (*File2).Time.dwHighDateTime<<9) | ( (*File2).Time.dwLowDateTime>>23); if ( ( ( Low1 > Low2) ? ( Low1 - Low2) : ( Low2 - Low1)) > Granularity) { strcat( (*File1).Flag, "T"); Differ = TRUE; } } else { Differ = TRUE; } } else if ( CompareFileTime( &( (*File1).Time), &( (*File2).Time)) != 0) { strcat( (*File1).Flag, "T"); Differ = TRUE; } } if ( fCheckSize && ( ( (*File1).SizeLow != (*File2).SizeLow) || ( (*File1).SizeHigh != (*File2).SizeHigh) ) ) { strcat( (*File1).Flag, "S"); Differ = TRUE; } if ( fCheckAttribs) { if ( ((*File1).Attributes ^ (*File2).Attributes) & NORMAL_ATTRIBUTES) { strcat( (*File1).Flag, "A"); Differ = TRUE; } } if ( fCheckBits) { // // Create Full Path Strings // _strrev( (*File1).Name); _strrev( (*File2).Name); if ( DealingWithDirectories) { ( Path1[strlen( Path1) - 1] == '\\') ? ( FullPath1 = MYSTRCAT( Path1, (*File1).Name)) : ( FullPath1 = COMBINETHREESTRINGS( Path1, "\\", (*File1).Name)); ( Path2[strlen( Path2) - 1] == '\\') ? ( FullPath2 = MYSTRCAT( Path2, (*File1).Name)) : ( FullPath2 = COMBINETHREESTRINGS( Path2, "\\", (*File1).Name)); } else { FullPath1 = MYSTRCAT( Path1, ""); FullPath2 = MYSTRCAT( Path2, ""); } _strrev( (*File1).Name); _strrev( (*File2).Name); if ( fIgnoreRs) { if ( ( (*File1).SizeLow != 0 || (*File1).SizeHigh != 0) && ( !BinaryCompare( FullPath1, FullPath2) ) ) { strcat( (*File1).Flag, "B"); Differ = TRUE; } } else { if ( ( (*File1).SizeLow != (*File2).SizeLow) || ( (*File1).SizeHigh != (*File2).SizeHigh) || ( ( (*File1).SizeLow != 0 || (*File1).SizeHigh != 0 ) && ( !BinaryCompare( FullPath1, FullPath2)) ) ) { strcat( (*File1).Flag, "B"); Differ = TRUE; } } FREE( FullPath1); FREE( FullPath2); } if ( fForce) { Differ = TRUE; } } return Differ; } // FilesDiffer // // CompLists Does the dirty work for CompDir // void CompLists( LinkedFileList *AddList, LinkedFileList *DelList, LinkedFileList *DifList, char *Path1, char *Path2) { LinkedFileList *TmpAdd, *TmpDel, TmpNode; char *FullPath1, *FullPath2; if ( ( DelList == NULL) || ( *DelList == NULL) || ( AddList == NULL) || ( *AddList == NULL)) { return; } TmpAdd = AddList; // pointer to keep track of position in addlist if ( *TmpAdd != NULL) { TmpAdd = &( **TmpAdd).First; } do { if ( DealingWithDirectories) { TmpDel = FindInList( ( **TmpAdd).Name, DelList); } else { TmpDel = DelList; } if ( TmpDel != NULL) { if ( FilesDiffer( *TmpAdd, *TmpDel, Path1, Path2)) { // // Combine Both Nodes together so they // can be printed out together // DuplicateNode( *TmpAdd, &TmpNode); DuplicateNode( *TmpDel, &( *TmpNode).DiffNode); AddToList( TmpNode, DifList); ( **TmpDel).Process = FALSE; ( **TmpAdd).Process = FALSE; } else { ( **TmpDel).Process = FALSE; ( **TmpAdd).Process = !ProcessModeDefault; } } else if ( SparseTree && ( ( **TmpAdd).Attributes & FILE_ATTRIBUTE_DIRECTORY)) { if ( !fDoNotRecurse) { _strrev( ( **TmpAdd).Name); ( Path1[strlen( Path1) - 1] == '\\') ? ( FullPath1 = MYSTRCAT( Path1, ( **TmpAdd).Name)) : ( FullPath1 = COMBINETHREESTRINGS( Path1, "\\", ( **TmpAdd).Name)); ( Path2[strlen( Path2) - 1] == '\\') ? ( FullPath2 = MYSTRCAT( Path2, ( **TmpAdd).Name)) : ( FullPath2 = COMBINETHREESTRINGS( Path2, "\\", ( **TmpAdd).Name)); _strrev( ( **TmpAdd).Name); CompDir( FullPath1, FullPath2); } } // if ( *TmpDel != NULL) TmpAdd = &( ( **TmpAdd).Next); } while ( *TmpAdd != NULL); } // CompLists // // CopyNode walks the source node and its children ( recursively) // and creats the appropriate parts on the destination node // void CopyNode ( char *Destination, LinkedFileList Source, char *FullPathSrc) { BOOL pend, CanDetectFreeSpace = TRUE; int i; DWORD sizeround; DWORD BytesPerCluster; ATTRIBUTE_TYPE Attributes; int LastErrorGot; __int64 freespac; char root[5] = {*Destination,':','\\','\0'}; DWORD cSecsPerClus, cBytesPerSec, cFreeClus, cTotalClus; if ( !GetDiskFreeSpace( root, &cSecsPerClus, &cBytesPerSec, &cFreeClus, &cTotalClus ) ) { CanDetectFreeSpace = FALSE; } else { freespac = ( (__int64)cBytesPerSec * (__int64)cSecsPerClus * (__int64)cFreeClus ); BytesPerCluster = cSecsPerClus * cBytesPerSec; } fprintf( stdout, "%s => %s\t", FullPathSrc, Destination); if ( CanDetectFreeSpace) { sizeround = (*Source).SizeLow; sizeround += BytesPerCluster - 1; sizeround /= BytesPerCluster; sizeround *= BytesPerCluster; if ( freespac < sizeround) { fprintf( stderr, "not enough space\n"); return; } } GET_ATTRIBUTES( Destination, Attributes); i = SET_ATTRIBUTES( Destination, Attributes & NONREADONLYSYSTEMHIDDEN ); i = 1; do { if ( !fCreateLink) { if ( !fBreakLinks) { pend = MyCopyFile( FullPathSrc, Destination, FALSE); } else { _unlink( Destination); pend = MyCopyFile( FullPathSrc, Destination, FALSE); } } else { if ( i == 1) { pend = MakeLink( FullPathSrc, Destination, FALSE); } else { pend = MakeLink( FullPathSrc, Destination, TRUE); } } if ( SparseTree && !pend) { if ( !MyCreatePath( Destination, FALSE)) { fprintf( stderr, "Unable to create path %s", Destination); ExitValue = 1; } } } while ( ( i++ < 2) && ( !pend) ); if ( !pend) { LastErrorGot = GetLastError (); if ( ( fCreateLink) && ( LastErrorGot == 1)) { fprintf( stderr, "Can only make links on NTFS and OFS"); } else if ( fCreateLink) { fprintf( stderr, "(error = %d)", LastErrorGot); } else { fprintf( stderr, "Copy Error (error = %d)", LastErrorGot); } ExitValue = 1; } if ( pend) { fprintf( stdout, "[OK]\n"); } else { fprintf( stderr, "\n"); } // // Copy attributes from Source to Destination // // GET_ATTRIBUTES( FullPathSrc, Attributes); if ( !fDontCopyAttribs) { i = SET_ATTRIBUTES( Destination, Source->Attributes); } else { i = SET_ATTRIBUTES( Destination, FILE_ATTRIBUTE_ARCHIVE); } } // CopyNode // // CreateFileList walks down list adding files as they are found // DWORD CreateFileList( LPVOID ThreadParameter) { PCFLStruct Parameter = ( PCFLStruct)ThreadParameter; LinkedFileList *List = Parameter->List; char *Path = Parameter->Path; LinkedFileList Node; char *String; ATTRIBUTE_TYPE Attributes; HANDLE handle; WIN32_FIND_DATA Buff; IF_GET_ATTR_FAILS( Path, Attributes) { return 0; } if ( Attributes & FILE_ATTRIBUTE_DIRECTORY) { ( Path[strlen( Path) - 1] != '\\') ? ( String = MYSTRCAT( Path,"\\*.*")) : ( String = MYSTRCAT( Path,"*.*")); handle = FIND_FIRST( String, Buff); } else { handle = FIND_FIRST( Path, Buff); } FREE( String); if ( handle != INVALID_HANDLE_VALUE) { // // Need to find the '.' or '..' directories and get them out of the way // do { if ( ( strcmp( Buff.cFileName, ".") != 0) && ( strcmp( Buff.cFileName, "..") != 0) && ( ((Buff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) || !fDoNotRecurse) ) { // // If extensions are defined we match them here // if ( ( !Excludes ) || ( Excludes && ( !Excluded( Buff.cFileName, Path)) ) ) { if ( ( !Matches ) || ( ( Buff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) || ( Matches && ( Matched( Buff.cFileName, Path)) ) ) { if ( !fIgnoreSlmFiles || ( (_stricmp( Buff.cFileName, "slm.ini") != 0) && (_stricmp( Buff.cFileName, "slm.dif") != 0) && (_stricmp( Buff.cFileName, "iedcache.slm.v6") != 0) ) ) { if ( fSpecAttribs) { if ( Buff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { CreateNode( &Node, &Buff); AddToList( Node, List); } else if ( NegativeCompareAttributeSet && CompareAttributeSet) { if ( !( Buff.dwFileAttributes & NegativeCompareAttribute) && ( ( Buff.dwFileAttributes & CompareAttribute) == CompareAttribute) ) { CreateNode( &Node, &Buff); AddToList( Node, List); } } else if ( CompareAttributeSet ) { if ( ( Buff.dwFileAttributes & CompareAttribute) == CompareAttribute) { CreateNode( &Node, &Buff); AddToList( Node, List); } } else if ( NegativeCompareAttributeSet ) { if ( !( Buff.dwFileAttributes & NegativeCompareAttribute) ) { CreateNode( &Node, &Buff); AddToList( Node, List); } } } else { CreateNode( &Node, &Buff); AddToList( Node, List); } } } } } } while ( FIND_NEXT( handle, Buff) == 0); } // ( handle != INVALID_HANDLE_VALUE) FindClose( handle); return 0; // This will exit this thread } // CreateFileList BOOL DelNode ( char *Path) { char *String; ATTRIBUTE_TYPE Attributes; HANDLE handle; WIN32_FIND_DATA Buff; IF_GET_ATTR_FAILS( Path, Attributes) return TRUE; if ( Attributes & FILE_ATTRIBUTE_DIRECTORY) { ( Path[strlen( Path) - 1] != '\\') ? ( String = MYSTRCAT( Path,"\\*.*")) : ( String = MYSTRCAT( Path,"*.*")); handle = FIND_FIRST( String, Buff); if ( handle == INVALID_HANDLE_VALUE) { fprintf( stderr, "%s is inaccesible\n", Path); ExitValue = 1; return FALSE; } FREE( String); do { // // Need to find the '.' or '..' directories and get them out of the way // if ( ( strcmp( Buff.cFileName, ".") != 0) && ( strcmp( Buff.cFileName, "..") != 0) ) { // // if directory is read-only, make it writable // if ( Attributes & FILE_ATTRIBUTE_READONLY) { if ( SET_ATTRIBUTES( Path, Attributes & ~FILE_ATTRIBUTE_READONLY) != 0) { break; } } String = COMBINETHREESTRINGS( Path, "\\", Buff.cFileName); if ( !DelNode( String)) { FREE( String); return FALSE; } else { FREE( String); } } } while ( FIND_NEXT( handle, Buff) == 0); FindClose( handle); if ( _rmdir( Path) != 0) { return FALSE; } } else { // // if file is read-only, make it writable // if ( Attributes & FILE_ATTRIBUTE_READONLY) { if ( SET_ATTRIBUTES( Path, Attributes & ~FILE_ATTRIBUTE_READONLY) != 0) { return FALSE; } } if ( _unlink( Path) != 0) { return FALSE; } } return TRUE; } // DelNode BOOL IsFlag( char *argv) { char String[MAX_PATH]; char *String1, *String2; char *TmpArg; char *ExcludeFile, *MatchFile; FILE *FileHandle; LinkedFileList Node; BOOL NegationFlagSet = FALSE; if ( ( *argv == '/') || ( *argv == '-')) { fMatching = FALSE; // If there's a new flag then that's the fExclude = FALSE; // end of the match/exclude list if ( strchr( argv, '?')) { Usage(); } TmpArg = _strlwr( argv); while ( *++TmpArg != '\0') { switch ( *TmpArg) { case 'a' : fCheckAttribs = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'b' : fCheckBits = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'c' : fScript = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'd' : fDoNotDelete = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'e' : fExecute = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'f' : fOnlyIfExists = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'g' : fIgnoreSlmFiles = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'h' : fDontCopyAttribs = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'i' : fIgnoreRs = !NegationFlagSet; if ( fIgnoreRs) { GetTempPath( MAX_PATH, String); RebasedFile2 = RebasedFile; GetTempFileName( String, NULL, 0, RebasedFile2); } NegationFlagSet = FALSE; break; case 'k' : fBreakLinks = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'l' : fCreateLink = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'm' : if ( NegationFlagSet) { fprintf ( stderr, "can't use - on /m option\n"); Usage(); } if ( ( *( TmpArg + 1) == ':') && ( *( TmpArg + 2) != '\0') ) { ( MatchFile = TmpArg + 2); while (isgraph( *( ++TmpArg + 1))) {} if ( ( FileHandle = fopen( MatchFile, "r")) == NULL) { fprintf( stderr, "cannot open %s\n", MatchFile); Usage(); } else { while ( fgets( String1 = String, MAX_PATH, FileHandle) != NULL) { while ( *( String2 = &( String1[ strspn( String1, " \n\r") ]))) { if ( *( String1 = &( String2[ strcspn( String2, " \n\r") ]))) { *String1++ = 0; CreateNameNode( &Node, String2); if ( strchr( String2, '*') != NULL) { AddToList( Node, &MStarList); } else { AddToList( Node, &MatchList); } } } } fclose( FileHandle) ; } } fMatching = TRUE; Matches = TRUE; break; case 'n' : fCreateNew = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'o' : fOpposite = !NegationFlagSet; ProcessModeDefault = !fOpposite; NegationFlagSet = FALSE; break; case 'p' : if ( NegationFlagSet) { fprintf ( stderr, "can't use - on /p option\n"); Usage(); } if ( *( TmpArg + 1) != '{') { fprintf ( stderr, "/p option improperly formatted\n"); Usage(); } TmpArg++; while ( *++TmpArg != '}') { switch ( *TmpArg) { case 'a' : if ( NegationFlagSet) { if ( !NegativeCompareAttributeSet) { NegativeCompareAttribute = FILE_ATTRIBUTE_ARCHIVE; NegativeCompareAttributeSet = TRUE; } else { NegativeCompareAttribute = NegativeCompareAttribute | FILE_ATTRIBUTE_ARCHIVE; } } else { if ( !CompareAttributeSet) { CompareAttribute = FILE_ATTRIBUTE_ARCHIVE; CompareAttributeSet = TRUE; } else { CompareAttribute = CompareAttribute | FILE_ATTRIBUTE_ARCHIVE; } } NegationFlagSet = FALSE; break; case 'r' : if ( NegationFlagSet) { if ( !NegativeCompareAttributeSet) { NegativeCompareAttribute = FILE_ATTRIBUTE_READONLY; NegativeCompareAttributeSet = TRUE; } else { NegativeCompareAttribute = NegativeCompareAttribute | FILE_ATTRIBUTE_READONLY; } } else { if ( !CompareAttributeSet) { CompareAttribute = FILE_ATTRIBUTE_READONLY; CompareAttributeSet = TRUE; } else { CompareAttribute = CompareAttribute | FILE_ATTRIBUTE_READONLY; } } NegationFlagSet = FALSE; break; case 'h' : if ( NegationFlagSet) { if ( !NegativeCompareAttributeSet) { NegativeCompareAttribute = FILE_ATTRIBUTE_HIDDEN; NegativeCompareAttributeSet = TRUE; } else { NegativeCompareAttribute = NegativeCompareAttribute | FILE_ATTRIBUTE_HIDDEN; } } else { if ( !CompareAttributeSet) { CompareAttribute = FILE_ATTRIBUTE_HIDDEN; CompareAttributeSet = TRUE; } else { CompareAttribute = CompareAttribute | FILE_ATTRIBUTE_HIDDEN; } } NegationFlagSet = FALSE; break; case 's' : if ( NegationFlagSet) { if ( !NegativeCompareAttributeSet) { NegativeCompareAttribute = FILE_ATTRIBUTE_SYSTEM; NegativeCompareAttributeSet = TRUE; } else { NegativeCompareAttribute = NegativeCompareAttribute | FILE_ATTRIBUTE_SYSTEM; } } else { if ( !CompareAttributeSet) { CompareAttribute = FILE_ATTRIBUTE_SYSTEM; CompareAttributeSet = TRUE; } else { CompareAttribute = CompareAttribute | FILE_ATTRIBUTE_SYSTEM; } } NegationFlagSet = FALSE; break; case '-' : NegationFlagSet = TRUE; break; default : fprintf( stderr, "/p option improperly formatted\n"); Usage(); } } if ( !CompareAttributeSet && !NegativeCompareAttributeSet) { fprintf( stderr, "no compare attributes not set\n"); Usage(); } fSpecAttribs = TRUE; NegationFlagSet = FALSE; break; case 'r' : fDoNotRecurse = !NegationFlagSet; NegationFlagSet = FALSE; break; case 's' : fCheckSize = !NegationFlagSet; NegationFlagSet = FALSE; break; case 't' : // // Get Granularity parameter // if ( ( *( TmpArg + 1) == ':') && ( *( TmpArg + 2) != '\0') ) { sscanf( ( TmpArg + 2), "%d", &Granularity); Granularity = Granularity*78125/65536; // Conversion to seconds ^^^^^^^ // 10^7/2^23 while (isdigit( *( ++TmpArg + 1))) {} } fCheckTime = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'u' : fMultiThread = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'v' : fVerbose = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'w' : fDontLowerCase = !NegationFlagSet; NegationFlagSet = FALSE; break; case 'x' : if ( NegationFlagSet) { fprintf ( stderr, "can't use - on /x option\n"); Usage(); } if ( ( *( TmpArg + 1) == ':') && ( *( TmpArg + 2) != '\0') ) { ( ExcludeFile = TmpArg + 2); while (isgraph( *( ++TmpArg + 1))) {} if ( ( FileHandle = fopen( ExcludeFile, "r")) == NULL) { fprintf( stderr, "cannot open %s\n", ExcludeFile); Usage(); } else { while ( fgets( String1 = String, MAX_PATH, FileHandle) != NULL) { while ( *( String2 = &( String1[ strspn( String1, "\n\r") ]))) { if ( *( String1 = &( String2[ strcspn ( String2, "\n\r") ]))) { *String1++ = 0; CreateNameNode( &Node, String2); if ( strchr( String2, '*') != NULL) { AddToList( Node, &EStarList); } else { AddToList( Node, &ExcludeList); } } } } fclose( FileHandle) ; } } fExclude = TRUE; Excludes = TRUE; break; case 'z' : fForce = !NegationFlagSet; NegationFlagSet = FALSE; break; case '$' : fTrySis = !NegationFlagSet; NegationFlagSet = FALSE; break; case '/' : NegationFlagSet = FALSE; break; case '-' : NegationFlagSet = TRUE; break; default : fprintf( stderr, "Don't know flag(s) %s\n", argv); Usage(); } } } else { return FALSE; } return TRUE; } // IsFlag BOOL Excluded( char *FileName, char *Path) { char *PathPlusName; PathPlusName = COMBINETHREESTRINGS( Path, "\\", FileName); if ( ( FindInMatchListTop( FileName, &ExcludeList)) || ( FindInMatchListTop( PathPlusName, &ExcludeList)) || ( FindInMatchListFront( FileName, &EStarList)) || ( FindInMatchListFront( PathPlusName, &EStarList)) ) { FREE( PathPlusName); return TRUE; } else { FREE( PathPlusName); return FALSE; } } // Excluded BOOL Matched( char *FileName, char *Path) { char *PathPlusName; PathPlusName = COMBINETHREESTRINGS( Path, "\\", FileName); if ( ( FindInMatchListTop( FileName, &MatchList)) || ( FindInMatchListTop( PathPlusName, &MatchList)) || ( FindInMatchListFront( FileName, &MStarList)) || ( FindInMatchListFront( PathPlusName, &MStarList)) ) { FREE( PathPlusName); return TRUE; } else { FREE( PathPlusName); return FALSE; } } // Matched BOOL MyCreatePath( char *Path, BOOL IsDirectory) { char *ShorterPath, *LastSlash; ATTRIBUTE_TYPE Attributes; IF_GET_ATTR_FAILS( Path, Attributes) { if ( !IsDirectory || ( ( _mkdir( Path)) != 0) ) { ShorterPath = MYSTRCAT( Path, ""); LastSlash = strrchr( ShorterPath, '\\'); if ( ( LastSlash != NULL) && ( LastSlash != strchr( ShorterPath, '\\')) ) { *LastSlash = '\0'; } else { FREE( ShorterPath); return FALSE; } if ( MyCreatePath( ShorterPath, TRUE)) { FREE( ShorterPath); if ( IsDirectory) { return( ( _mkdir( Path)) == 0); } else { return TRUE; } } else { _rmdir( ShorterPath); FREE( ShorterPath); return FALSE; } } else { return TRUE; } } else { return TRUE; } } // MyCreatePath BOOL MyCopyFile( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists ) { BOOL ok; if (fTrySis) { ok = SisCopyFile( lpExistingFileName, lpNewFileName, bFailIfExists, &fTrySis); if (ok) { return TRUE; } } return CopyFile( lpExistingFileName, lpNewFileName, bFailIfExists); } int ParseArgsSub( int argc, char *argv[]) { int ArgCount, FlagCount; LinkedFileList Node; ArgCount = 1; FlagCount = 0; fMatching = FALSE; fExclude = FALSE; do { if ( IsFlag( argv[ArgCount] )) { FlagCount++; } else // ( IsFlag( argv[ArgCount] )) { if ( ArgCount + 2 < argc) { if ( fMatching) { CreateNameNode( &Node, argv[ArgCount]); if ( strchr( argv[ArgCount], '*') != NULL) { AddToList( Node, &MStarList); } else { AddToList( Node, &MatchList); } } if ( fExclude) { CreateNameNode( &Node, argv[ArgCount]); if ( strchr( argv[ArgCount], '*') != NULL) { AddToList( Node, &EStarList); } else { AddToList( Node, &ExcludeList); } } if ( ( !fMatching) && ( !fExclude)) { fprintf( stderr, "Don't know option %s\n", argv[ArgCount]); Usage(); } } } } while ( ArgCount++ < argc - 1); return FlagCount; } // ParseArgsSub void ParseEnvArgs( void) { int argc; char *argv[128]; char env[MAX_PATH+2]; char *p; int ArgCount, FlagCount; LinkedFileList Node; if ( !GetEnvironmentVariable( "COMPDIRCMD", env, MAX_PATH+2)) { return; } argc = 1; p = env; while ( (*p != 0) && isspace(*p)) { p++; } while ( *p) { argv[argc++] = p++; while ( (*p != 0) && !isspace(*p)) { p++; } if ( *p != 0) { *p++ = 0; while ( (*p != 0) && isspace(*p)) { p++; } } } ParseArgsSub( argc, argv); } // ParseEnvArgs void ParseArgs( int argc, char *argv[]) { int FlagCount; // // Check that number of arguments is three or more // if ( argc < 3) { fprintf( stderr, "Too few arguments\n"); Usage(); } FlagCount = ParseArgsSub( argc, argv); if ( ( fScript) && ( fVerbose)) { fprintf( stderr, "Cannot do both script and verbose\n"); Usage(); } if ( ( fVerbose) && ( fExecute)) { fprintf( stderr, "Cannot do both verbose and execute\n"); Usage(); } if ( ( fScript) && ( fExecute)) { fprintf( stderr, "Cannot do both script and execute\n"); Usage(); } if ( ( fExclude) && ( fMatching)) { fprintf( stderr, "Cannot do both match and exclude\n"); Usage(); } if ( ( fCreateNew) && ( !fExecute)) { fprintf( stderr, "Cannot create new without execute\n"); Usage(); } if ( ( fCreateLink) && ( !fExecute)) { fprintf( stderr, "Cannot do link without execute flag\n"); Usage(); } if ( ( fForce) && ( !fExecute)) { fprintf( stderr, "Cannot do force without execute flag\n"); Usage(); } if ( ( fIgnoreRs) && ( !fCheckBits)) { fprintf( stderr, "Cannot ignore rebase info w/o b flag\n"); Usage(); } if ( ( fBreakLinks) && ( !fExecute)) { fprintf( stderr, "Cannot break links without execute flag\n"); Usage(); } if ( ( argc - FlagCount) < 3) { fprintf( stderr, "Too few arguments\n"); Usage(); } fChecking = fCheckAttribs | fCheckBits | fCheckSize | fCheckTime; } // ParseArgs void PrintFile( LinkedFileList File, char *Path, char *DiffPath) { SYSTEMTIME SysTime; FILETIME LocalTime; if ( File != NULL) { if ( fVerbose) { FileTimeToLocalFileTime( &( *File).Time, &LocalTime); FileTimeToSystemTime( &LocalTime, &SysTime); fprintf ( stdout, "%-4s % 9ld %2d-%02d-%d %2d:%02d.%02d.%03d%c %s\n", ( *File).Flag, ( *File).SizeLow, SysTime.wMonth, SysTime.wDay, SysTime.wYear, ( SysTime.wHour > 12 ? ( SysTime.wHour)-12 : SysTime.wHour ), SysTime.wMinute, SysTime.wSecond, SysTime.wMilliseconds, ( SysTime.wHour >= 12 ? 'p' : 'a' ), Path); } else { fprintf( stdout, "%-4s %s\n", ( *File).Flag, Path); } PrintFile( ( *File).DiffNode, DiffPath, NULL); } } // PrintFile void ProcessAdd( LinkedFileList List, char *String1, char *String2) { PCOPY_REQUEST CopyRequest; LPSTR NewString1, NewString2; if ( fMultiThread) { NewString1 = _strdup( String1); NewString2 = _strdup( String2); } if ( fScript) { if ( ( (*List).Attributes & FILE_ATTRIBUTE_DIRECTORY)) { if ( !fOpposite) { fprintf( stdout, "echo d | xcopy /cehikr \"%s\" \"%s\"\n", String1, String2); } } else { fprintf( stdout, "echo f | xcopy /cehikr \"%s\" \"%s\"\n", String1, String2); } } else if ( fExecute) { if ( List->Attributes & FILE_ATTRIBUTE_DIRECTORY) { if ( ( !fDoNotRecurse) && ( !fOpposite)) { if ( !SparseTree) { fprintf( stdout, "Making %s\t", String2); if ( !MyCreatePath( String2, TRUE)) { fprintf( stderr, "Unable to create path %s\n", String2); fprintf( stdout, "\n"); ExitValue = 1; } else { fprintf( stdout, "[OK]\n"); CompDir( String1, String2); } } else { CompDir( String1, String2); } } } else { if ( fMultiThread) { CopyRequest = LocalAlloc( LMEM_ZEROINIT, sizeof( *CopyRequest )); if ( CopyRequest == NULL) { OutOfMem (); } CopyRequest->WorkItem.Reason = WORK_ITEM; CopyRequest->Destination = NewString2; CopyRequest->FullPathSrc = NewString1; CopyRequest->Attributes = List->Attributes; CopyRequest->SizeLow = List->SizeLow; QueueWorkItem( CDWorkQueue, &CopyRequest->WorkItem ); } else { CopyNode( String2, List, String1); free( NewString1 ); free( NewString2 ); } } } else { if ( ( !fOpposite) || ( !( (*List).Attributes & FILE_ATTRIBUTE_DIRECTORY))) { PrintFile( List, String1, NULL); } } } // ProcessAdd void ProcessDel( LinkedFileList List, char *String) { if ( fScript) { ( ( (*List).Attributes & FILE_ATTRIBUTE_DIRECTORY)) ? fprintf( stdout, "echo y | rd /s %s\n", String) : fprintf( stdout, "del /f %s\n", String); } else if ( fExecute) { fprintf( stdout, "Removing %s\t", String); if ( !DelNode( String)) { fprintf( stderr, "Unable to remove %s\n", String); fprintf( stdout, "\n"); ExitValue = 1; } else { fprintf( stdout, "[OK]\n"); } } else { PrintFile( List, String, NULL); } } // ProcessDel void ProcessDiff( LinkedFileList List, char *String1, char *String2) { PCOPY_REQUEST CopyRequest; LPSTR NewString1, NewString2; if ( fMultiThread) { NewString1 = _strdup( String1); NewString2 = _strdup( String2); } if ( strchr ( (*List).Flag, '@')) { if ( fScript) { if ( ( (*List).Attributes & FILE_ATTRIBUTE_DIRECTORY)) { fprintf( stdout, "echo y | rd /s %s\n", String2); } else { fprintf( stdout, "del /f %s\n", String2); } } if ( fExecute) { fprintf( stdout, "Removing %s\t", String2); if ( !DelNode( String2)) { fprintf( stderr, "Unable to remove %s\n", String2); fprintf( stdout, "\n"); ExitValue = 1; } else { fprintf( stdout, "[OK]\n"); } } } if ( fScript) { ( ( (*List).Attributes & FILE_ATTRIBUTE_DIRECTORY)) ? fprintf( stdout, "echo d | xcopy /cehikr \"%s\" \"%s\"\n", String1, String2) : fprintf( stdout, "echo f | xcopy /cehikr \"%s\" \"%s\"\n", String1, String2); } else if ( fExecute) { if ( List->Attributes & FILE_ATTRIBUTE_DIRECTORY) { CompDir( String1, String2); } else { if ( fMultiThread) { CopyRequest = LocalAlloc( LMEM_ZEROINIT, sizeof( *CopyRequest ) ); if ( CopyRequest == NULL) { OutOfMem (); } CopyRequest->WorkItem.Reason = WORK_ITEM; CopyRequest->Destination = NewString2; CopyRequest->FullPathSrc = NewString1; CopyRequest->Attributes = List->Attributes; CopyRequest->SizeLow = List->SizeLow; QueueWorkItem( CDWorkQueue, &CopyRequest->WorkItem ); } else { CopyNode( String2, List, String1); free( NewString1 ); free( NewString2 ); } } } else { PrintFile( List, String1, String2); } } // ProcessDiff void ProcessLists( LinkedFileList AddList, LinkedFileList DelList, LinkedFileList DifList, char *Path1, char *Path2 ) { LinkedFileList PlaceKeeper; char *String1 = NULL; char *String2 = NULL; char *PathWithSlash1, *PathWithSlash2; ( Path1[strlen( Path1) - 1] == '\\') ? ( PathWithSlash1 = MYSTRCAT( Path1, "")) : ( PathWithSlash1 = MYSTRCAT( Path1, "\\")); ( Path2[strlen( Path2) - 1] == '\\') ? ( PathWithSlash2 = MYSTRCAT( Path2, "")) : ( PathWithSlash2 = MYSTRCAT( Path2, "\\")); String1 = LocalAlloc( LMEM_ZEROINIT, MAX_PATH); String2 = LocalAlloc( LMEM_ZEROINIT, MAX_PATH); if ( String1 == NULL) { OutOfMem(); } if ( String2 == NULL) { OutOfMem(); } if ( !fOnlyIfExists) { if ( AddList != NULL) { PlaceKeeper = ( *AddList).First; } else { PlaceKeeper = NULL; } while ( PlaceKeeper != NULL) { if ( ( *PlaceKeeper).Process) { if ( ExitValue == 0) { if ( !fExecute) { ExitValue = 1; } } _strrev( ( *PlaceKeeper).Name); strcat( strcpy( String1, PathWithSlash1), ( *PlaceKeeper).Name); strcat( strcpy( String2, PathWithSlash2), ( *PlaceKeeper).Name); if ( DealingWithDirectories) { ProcessAdd( PlaceKeeper, String1, String2); } else { ProcessAdd( PlaceKeeper, Path1, Path2); } } PlaceKeeper = ( *PlaceKeeper).Next; } } if ( ( !fDoNotDelete) && ( !fOnlyIfExists)) { if ( DelList != NULL) { PlaceKeeper = ( *DelList).First; } else { PlaceKeeper = NULL; } while ( PlaceKeeper != NULL) { if ( ( *PlaceKeeper).Process) { if ( ExitValue == 0) { if ( !fExecute) { ExitValue = 1; } } _strrev( ( *PlaceKeeper).Name); strcat( strcpy( String2, PathWithSlash2), ( *PlaceKeeper).Name); ProcessDel( PlaceKeeper, String2); } PlaceKeeper = ( *PlaceKeeper).Next; } } if ( DifList != NULL) { PlaceKeeper = ( *DifList).First; } else { PlaceKeeper = NULL; } while ( PlaceKeeper != NULL) { if ( ( *PlaceKeeper).Process) { if ( ExitValue == 0) { if ( !fExecute) { ExitValue = 1; } } _strrev( ( *PlaceKeeper).Name); strcat( strcpy( String1, PathWithSlash1), ( *PlaceKeeper).Name); strcat( strcpy( String2, PathWithSlash2), ( *PlaceKeeper).Name); if ( DealingWithDirectories) { ProcessDiff( PlaceKeeper, String1, String2); } else { ProcessDiff( PlaceKeeper, Path1, Path2); } } PlaceKeeper = ( *PlaceKeeper).Next; } LocalFree( String1); LocalFree( String2); FREE( PathWithSlash1); FREE( PathWithSlash2); } // ProcessLists void Usage( void) { fprintf( stderr, "Usage: compdir [/abcdefghiklnoprstuvwz$] [/m {wildcard specs}] [/x {wildcard specs}] Path1 Path2 \n"); fprintf( stderr, " /a checks for attribute difference \n"); fprintf( stderr, " /b checks for binary difference \n"); fprintf( stderr, " /c prints out script to make \n"); fprintf( stderr, " directory2 look like directory1 \n"); fprintf( stderr, " /d do not perform or denote deletions \n"); fprintf( stderr, " /e execution of tree duplication \n"); fprintf( stderr, " /f only update files that already exist \n"); fprintf( stderr, " /g ignore slm files, i.e slm.ini, slm.dif\n"); fprintf( stderr, " /h don't copy attributes \n"); fprintf( stderr, " /i ignore rebase and resource differences\n"); fprintf( stderr, " /k break links if copying files (NT only)\n"); fprintf( stderr, " /l use links instead of copies (NT only)\n"); fprintf( stderr, " /m[:f] marks start of match list. f is a \n"); fprintf( stderr, " match file \n"); fprintf( stderr, " /n create second path if it doesn't exist\n"); fprintf( stderr, " /o print files that are the same \n"); fprintf( stderr, " /p{A} only compare files with attribute A \n"); fprintf( stderr, " where A is any combination of ahsr & -\n"); fprintf( stderr, " /r do not recurse into subdirectories \n"); fprintf( stderr, " /s checks for size difference \n"); fprintf( stderr, " /t[:#] checks for time-date difference; \n"); fprintf( stderr, " takes margin-of-error parameter \n"); fprintf( stderr, " in number of seconds. \n"); fprintf( stderr, " /u uses multiple threads (Win32 only) \n"); fprintf( stderr, " /v prints verbose output \n"); fprintf( stderr, " /w preserves case - not just lower case \n"); fprintf( stderr, " /x[:f] marks start of exclude list. f is an \n"); fprintf( stderr, " exclude file \n"); fprintf( stderr, " /z forces copy or link without checking \n"); fprintf( stderr, " criteria \n"); fprintf( stderr, " /$ create SIS links if possible \n"); fprintf( stderr, " /? prints this message \n"); exit(1); } // Usage