windows-nt/Source/XPSP1/NT/sdktools/suck/suck.c
2020-09-26 16:20:57 +08:00

1451 lines
41 KiB
C

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// #define SEE_EM 1
#define MAX_FILENAME_LENGTH 127
#define MAX_TIMEANDSIZE 50 /* mm-dd-yyyy hh-mm-ss */
#define MAX_FILENAME_PER_BLOCK 1000
#define MAX_MEMBLOCKS 100
#define MAX_THREADS 29
#define MAX_COMMAND_LINE 1024
#define MAX_ARGS 20
// kenhia 15-Mar-1996: add support for -#:<share>
//
// splante 15-Oct-1996: changed support to the "ntbuilds" server
#if defined(_ALPHA_) || defined(_X86_)
#define PLATFORM_SPECIFIC_SHARES { "ntbuilds", "ntbuilds", "ntbuilds", "ntbuilds", "ntbuilds", NULL }
#endif
#ifndef PLATFORM_SPECIFIC_SHARES
#pragma message( "WARNING: Platform Specific shares disabled" )
#define PLATFORM_SPECIFIC_SHARES {NULL}
#endif
typedef struct _filename FILENAME;
typedef union _virtual_pointer {
FILENAME *mem_ptr;
DWORD disk_ptr;
} VIRTPTR;
struct _filename {
DWORD dwStatus;
DWORD dwCopy;
VIRTPTR fnParent;
VIRTPTR fnChild;
VIRTPTR fnSibling;
DWORD dwFileSizeLow;
DWORD dwFileSizeHigh;
FILETIME ftFileTime;
DWORD dwDATFileSizeLow;
DWORD dwDATFileSizeHigh;
FILETIME ftDATFileTime;
DWORD dwFileNameLen;
CHAR cFileName[MAX_FILENAME_LENGTH+1];
};
typedef struct _memblock MEMBLOCK;
struct _memblock {
HANDLE hMem;
LPSTR lpBase;
};
typedef struct _fileheader {
VIRTPTR fnRoot; // Pointer to root node
} FILEHEADER;
#define SUCK_INI_FILE ".\\suck.ini"
#define SUCK_DAT_FILE ".\\suck.dat"
#define DIRECTORY (DWORD)0x80000000
#define STARTED (DWORD)0x40000000
#define COPIED (DWORD)0x20000000
CRITICAL_SECTION cs;
CRITICAL_SECTION pcs;
INT cAvailable = 0;
FILENAME *lpBaseCurrent = NULL;
INT nMemBlocks = 0;
FILENAME *fnRoot = NULL;
LONG nFiles = 0;
LONG nDirectories = 0;
LONG nDuplicates = 0;
LONG nStraglers = 0;
BOOL fCopying = FALSE;
BOOL fScriptMode = FALSE;
BOOL fLogTreeDifferences = FALSE;
BOOL fUpdateINIBase = FALSE;
BOOL fDestroy = FALSE;
BOOL fUseDAT = TRUE;
// DavidP 23-Jan-1998: BEGIN Allow multiple levels of quiet
BOOL fQuietMode = FALSE;
BOOL fProgressMode = FALSE;
INT nConsoleWidth = 0;
CHAR chLineEnd = '\n';
// DavidP 23-Jan-1998: END Allow multiple levels of quiet
FILE *SuckDATFile = NULL;
#define MAX_EXCLUDES 1024
CHAR gExcludes[MAX_EXCLUDES+1] = { '\0'};
FILETIME ftZero = { 0, 0};
MEMBLOCK mbBlocks[MAX_MEMBLOCKS];
DWORD dwMasks[] = {
0x00000001,
0x00000002,
0x00000004,
0x00000008,
0x00000010,
0x00000020,
0x00000040,
0x00000080,
0x00000100,
0x00000200,
0x00000400,
0x00000800,
0x00001000,
0x00002000,
0x00004000,
0x00008000,
0x00010000,
0x00020000,
0x00040000,
0x00080000,
0x00100000,
0x00200000,
0x00400000,
0x00800000,
0x01000000,
0x02000000,
0x04000000,
0x08000000,
0x10000000,
};
CHAR chPath[MAX_THREADS][MAX_PATH];
DWORD dwTotalSizes[MAX_THREADS];
BOOL EverybodyBailOut = FALSE;
BOOL fProblems[MAX_THREADS];
FILENAME *AllocateFileName()
{
FILENAME *lpResult;
/*
** Allocate a new FILENAME
*/
if ( cAvailable == 0 ) {
mbBlocks[nMemBlocks].hMem = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT,
MAX_FILENAME_PER_BLOCK * sizeof(FILENAME) );
if ( mbBlocks[nMemBlocks].hMem == (HANDLE)0 ) {
fprintf(stderr,"Memory Allocation Failed in AllocateFileName\n");
exit(1);
}
mbBlocks[nMemBlocks].lpBase = GlobalLock( mbBlocks[nMemBlocks].hMem );
lpBaseCurrent = (FILENAME *)mbBlocks[nMemBlocks].lpBase;
cAvailable = MAX_FILENAME_PER_BLOCK;
nMemBlocks++;
}
lpResult = lpBaseCurrent;
--cAvailable;
lpBaseCurrent++;
return( lpResult );
}
VOID FreeFileNames()
{
while ( nMemBlocks ) {
--nMemBlocks;
GlobalUnlock( mbBlocks[nMemBlocks].hMem );
GlobalFree( mbBlocks[nMemBlocks].hMem );
}
}
VOID
AddFile(
FILENAME *fn,
LPWIN32_FIND_DATA lpwfd,
DWORD mask
)
{
CHAR *pdest;
CHAR *psrc;
INT count;
INT maximum;
FILENAME *fnCurrent;
FILENAME *fnChild;
DWORD dwFileNameLen;
CHAR NewName[MAX_FILENAME_LENGTH+1];
FILENAME *fnChildOriginally;
if ( *lpwfd->cFileName == '.' ) {
return;
}
dwFileNameLen = strlen(lpwfd->cFileName);
if (dwFileNameLen > MAX_FILENAME_LENGTH) {
fprintf(stderr, "File name %s too long (%u > %u), complain to BobDay\n",
lpwfd->cFileName, dwFileNameLen, MAX_FILENAME_LENGTH);
return;
}
strcpy( NewName, lpwfd->cFileName );
fnChild = fn->fnChild.mem_ptr;
fnChildOriginally = fnChild;
while ( fnChild ) {
if ( fnChild->dwFileNameLen == dwFileNameLen &&
!strcmp(NewName, fnChild->cFileName)
) {
fnChild->dwStatus |= mask; // Atomic instruction
if ( fnChild->ftFileTime.dwLowDateTime == ftZero.dwLowDateTime
&& fnChild->ftFileTime.dwHighDateTime == ftZero.dwHighDateTime ) {
EnterCriticalSection( &cs );
fnChild->dwFileSizeLow = lpwfd->nFileSizeLow;
fnChild->dwFileSizeHigh = lpwfd->nFileSizeHigh;
fnChild->ftFileTime = lpwfd->ftLastWriteTime;
LeaveCriticalSection( &cs );
}
nDuplicates++;
return;
}
fnChild = fnChild->fnSibling.mem_ptr;
}
// Probably not there... Enter the critical section now to prove it
EnterCriticalSection( &cs );
// Most common case, nobody has changed this directory at all.
if ( fn->fnChild.mem_ptr != fnChildOriginally ) {
// Otherwise, make another scan inside the critical section.
fnChild = fn->fnChild.mem_ptr;
while ( fnChild ) {
if ( fnChild->dwFileNameLen == dwFileNameLen &&
!strcmp(NewName, fnChild->cFileName)
) {
fnChild->dwStatus |= mask; // Atomic instruction
nDuplicates++;
LeaveCriticalSection( &cs );
return;
}
fnChild = fnChild->fnSibling.mem_ptr;
}
}
fnCurrent = AllocateFileName();
strcpy( fnCurrent->cFileName, NewName );
fnCurrent->dwFileNameLen = dwFileNameLen;
fnCurrent->dwFileSizeLow = lpwfd->nFileSizeLow;
fnCurrent->dwFileSizeHigh = lpwfd->nFileSizeHigh;
fnCurrent->ftFileTime = lpwfd->ftLastWriteTime;
fnCurrent->dwDATFileSizeLow = 0;
fnCurrent->dwDATFileSizeHigh = 0;
fnCurrent->ftDATFileTime = ftZero;
fnCurrent->dwCopy = 0;
fnCurrent->fnParent.mem_ptr = fn;
fnCurrent->fnChild.mem_ptr = NULL;
fnCurrent->fnSibling.mem_ptr = fn->fnChild.mem_ptr;
fn->fnChild.mem_ptr = fnCurrent;
if ( lpwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
fnCurrent->dwStatus = DIRECTORY;
nDirectories++;
} else {
fnCurrent->dwStatus = 0;
nFiles++;
}
fnCurrent->dwStatus |= mask;
#ifdef SEE_EM
{ char text[MAX_FILENAME_LENGTH+1];
memcpy( text, fnCurrent->cFileName, MAX_FILENAME_LENGTH );
text[MAX_FILENAME_LENGTH] = '\0';
if ( fnCurrent->dwStatus & DIRECTORY ) {
printf("Munged DirName = %08lX:[%s]\n", fnCurrent, text );
} else {
printf("Munged FileName = %08lX:[%s]\n", fnCurrent, text );
}
}
#endif
LeaveCriticalSection( &cs );
}
BOOL
Excluded(
WIN32_FIND_DATA *pwfd
)
{
CHAR *pszScan = gExcludes;
while (*pszScan) {
if ((pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
_stricmp(pszScan, pwfd->cFileName) == 0) {
return(TRUE);
}
pszScan = strchr(pszScan, 0) + 1;
}
return(FALSE);
}
VOID
EnumFiles(
LPSTR lpSearch,
FILENAME *fnParent,
DWORD mask,
UINT iThread
)
{
WIN32_FIND_DATA wfd;
HANDLE hFind;
CHAR NewName[MAX_PATH];
CHAR *pch;
CHAR *pchSpot;
BOOL f;
FILENAME *fnChild;
DWORD rc;
#ifdef SEE_EM
printf("Enuming <%s>\n", lpSearch );
#endif
strcpy( NewName, lpSearch );
pch = NewName + strlen(NewName) - 1;
if ( *pch != '\\' && *pch != '/' && *pch != ':' ) {
*++pch = '\\';
}
strcpy( ++pch, "*.*" );
pchSpot = pch;
do {
hFind = FindFirstFile( NewName, &wfd );
if ( hFind != INVALID_HANDLE_VALUE ) {
break;
}
rc = GetLastError();
switch ( rc ) {
default:
printf("%s: Error: GetLastError = %08ld What does it mean?\n", NewName, rc );
case ERROR_SHARING_PAUSED:
case ERROR_BAD_NETPATH:
case ERROR_BAD_NET_NAME:
case ERROR_NO_LOGON_SERVERS:
case ERROR_VC_DISCONNECTED:
case ERROR_UNEXP_NET_ERR:
if ( !fProblems[iThread] ) {
printf("Error accesing %s, switching to silent retry\n", lpSearch );
fProblems[iThread] = TRUE;
}
if (EverybodyBailOut) {
return;
}
Sleep( 10000 ); // Wait for 10 seconds
break;
break;
}
} while ( TRUE );
if ( hFind != NULL ) {
do {
if (!Excluded(&wfd))
AddFile( fnParent, &wfd, mask );
f = FindNextFile( hFind, &wfd );
} while ( f );
}
FindClose( hFind );
fnChild = fnParent->fnChild.mem_ptr;
while ( fnChild ) {
/*
** If its a directory and it was one of "our" directories, then enum it
*/
if ( (fnChild->dwStatus & DIRECTORY) == DIRECTORY
&& (fnChild->dwStatus & mask) == mask ) {
pch = pchSpot;
strcpy( pch, fnChild->cFileName );
#ifdef SEE_EM
printf("NewName = <%s>\n", NewName );
#endif
EnumFiles( NewName, fnChild, mask, iThread );
}
fnChild = fnChild->fnSibling.mem_ptr;
}
}
BOOL
CopyCheck(
CHAR *pchPath,
FILENAME *fnCurrent
)
{
WORD wFatDate;
WORD wFatTime;
WORD wDATFatDate;
WORD wDATFatTime;
BOOL b;
if ( fnCurrent->dwDATFileSizeLow != fnCurrent->dwFileSizeLow ) {
return( TRUE );
}
if ( fnCurrent->dwDATFileSizeHigh != fnCurrent->dwFileSizeHigh ) {
return( TRUE );
}
b = FileTimeToDosDateTime( &fnCurrent->ftDATFileTime, &wDATFatDate, &wDATFatTime );
if ( !b ) {
return( TRUE );
}
b = FileTimeToDosDateTime( &fnCurrent->ftFileTime, &wFatDate, &wFatTime );
if ( !b ) {
return( TRUE );
}
if ( wDATFatTime != wFatTime ) {
return( TRUE );
}
if ( wDATFatDate != wFatDate ) {
return( TRUE );
}
return( FALSE );
}
DWORD
CopyThem(
FILENAME *fnDir,
CHAR *chDest,
CHAR *chSrc,
DWORD nThread,
DWORD mask,
BOOL f1stPass
)
{
CHAR *pch;
CHAR *pchSpotDest;
CHAR *pchSpotSrc;
CHAR chTemp[MAX_PATH];
CHAR chTempName[20];
BOOL fCopyIt;
FILENAME *fnChild;
BOOL fCopy;
BOOL fCopied;
BOOL fRenamed;
BOOL fDeleted;
BOOL fAttrib;
DWORD dwCount;
DWORD dwAttribs;
DWORD dw;
fnChild = fnDir->fnChild.mem_ptr;
pchSpotDest = chDest + strlen(chDest);
pchSpotSrc = chSrc + strlen(chSrc);
dwCount = 0;
while ( fnChild && !EverybodyBailOut) {
fCopyIt = TRUE;
if ( f1stPass ) {
if ( (fnChild->dwStatus & STARTED) == STARTED ) {
fCopyIt = FALSE;
}
} else {
if ( (fnChild->dwStatus & COPIED) == COPIED ) {
fCopyIt = FALSE;
}
}
//
// If the file doesn't exist on this thread's source location, then
// don't try to copy it.
//
if ( (fnChild->dwStatus & mask) != mask ) {
fCopyIt = FALSE;
}
if ( fCopyIt ) {
// if ( f1stPass && (fnChild->dwStatus & STARTED) == STARTED ) {
// fCopyIt = FALSE;
// } else {
// fnChild->dwStatus |= STARTED;
// }
// LeaveCriticalSection( &pcs );
}
if ( fCopyIt ) {
pch = pchSpotDest;
strcpy( pch, fnChild->cFileName );
strcpy( pchSpotSrc, pchSpotDest );
if ( (fnChild->dwStatus & DIRECTORY) == DIRECTORY ) {
CreateDirectory( chDest, NULL );
strcat( pchSpotDest, "\\" );
strcat( pchSpotSrc, "\\" );
dwCount += CopyThem( fnChild, chDest, chSrc, nThread, mask, f1stPass );
} else {
fnChild->dwStatus |= STARTED;
strcpy( chTemp, chDest );
*(chTemp+(pchSpotDest-chDest)) = '\0';
sprintf( chTempName, "suck%02lX.tmp", mask );
strcat( chTemp, chTempName );
//
// Check if we need to copy this file
//
fCopy = CopyCheck( chDest, fnChild );
if ( fScriptMode ) {
dwCount++;
EnterCriticalSection( &pcs );
fnChild->dwStatus |= COPIED;
if ( fCopy ) {
if ( !fQuietMode ) {
printf("copy %s %s\n", chSrc, chDest );
}
dwTotalSizes[nThread-1] += fnChild->dwFileSizeLow;
} else {
if ( !fQuietMode ) {
printf("rem copy %s %s\n", chSrc, chDest );
}
}
LeaveCriticalSection( &pcs );
} else {
dwCount++;
if ( fCopy ) {
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
fCopied = CopyFile( chSrc, chTemp, FALSE );
if ( !fCopying ) {
EnterCriticalSection( &pcs );
if ( !fCopying ) {
fCopying = TRUE;
printf("Copying files...\n" );
}
LeaveCriticalSection( &pcs );
}
if ( !fCopied ) {
dw = GetLastError();
printf("%s => %s\t[COPY ERROR %08lX]\n", chSrc, chTemp, dw );
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
DeleteFile( chTemp );
switch ( dw ) {
case ERROR_BAD_NETPATH:
case ERROR_BAD_NET_NAME:
if ( !fProblems[nThread-1] ) {
printf("Error accesing %s, switching to silent attempts\n", chSrc );
fProblems[nThread-1] = TRUE;
}
Sleep( 10000 ); // Wait for 10 seconds
break;
default:
break;
}
} else {
EnterCriticalSection( &pcs );
if ( (fnChild->dwStatus & COPIED) == COPIED ) {
//
// Copy was done by somebody else
//
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
fDeleted = DeleteFile( chTemp );
} else {
//
// Copy was done by us, attempt rename
//
fAttrib = TRUE;
if ( fDestroy ) {
dwAttribs = GetFileAttributes( chDest );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chDest, dwAttribs );
}
}
if ( !fAttrib ) {
dw = GetLastError();
printf("%s => %s\t[ATTRIBUTE CHANGE ERROR %08lX(%s)\n", chSrc, chDest, dw, chDest );
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
fDeleted = DeleteFile( chTemp );
} else {
fDeleted = DeleteFile( chDest );
if ( !fDeleted ) {
dw = GetLastError();
fnChild->dwStatus |= COPIED;
}
if ( fDeleted || dw == ERROR_FILE_NOT_FOUND ) {
fRenamed = MoveFile( chTemp, chDest );
if ( fRenamed ) {
fnChild->dwStatus |= COPIED;
if ( !fQuietMode ) {
// DavidP 23-Jan-1998: Allow multiple levels of quiet
printf("%*s\r%s => %s\t[OK]%c", nConsoleWidth, "", chSrc, chDest, chLineEnd );
}
dwTotalSizes[nThread-1] += fnChild->dwFileSizeLow;
} else {
dw = GetLastError();
printf("%s => %s\t[RENAME ERROR %08lX (%s)]\n", chSrc, chDest, dw, chTemp );
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
fDeleted = DeleteFile( chTemp );
}
} else {
dw = GetLastError();
printf("%s => %s\t[DELETE ERROR %08lX (%s)]\n", chSrc, chDest, dw, chDest );
dwAttribs = GetFileAttributes( chTemp );
if ( dwAttribs & FILE_ATTRIBUTE_READONLY && dwAttribs != 0xFFFFFFFF ) {
dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
fAttrib = SetFileAttributes( chTemp, dwAttribs );
}
fDeleted = DeleteFile( chTemp );
}
}
}
LeaveCriticalSection( &pcs );
}
} else {
EnterCriticalSection( &pcs );
if ( !fCopying ) {
fCopying = TRUE;
printf("Copying files...\n" );
}
fnChild->dwStatus |= COPIED;
if ( !fQuietMode ) {
// DavidP 23-Jan-1998: Allow multiple levels of quiet
// printf("%*s\r%s => %s\t[OK]%c", nConsoleWidth, "", chSrc, chDest, chLineEnd );
}
LeaveCriticalSection( &pcs );
}
}
}
*pchSpotDest = '\0';
*pchSpotSrc = '\0';
}
fnChild = fnChild->fnSibling.mem_ptr;
}
return( dwCount );
}
DWORD
WINAPI
ThreadFunction(
LPVOID lpParameter
)
{
LPSTR lpSearch;
DWORD mask;
DWORD dw;
CHAR chDest[MAX_PATH];
CHAR chSrc[MAX_PATH];
DWORD dwCount;
dw = (DWORD)(DWORD_PTR)lpParameter;
lpSearch = chPath[dw];
mask = dwMasks[dw-1];
EnumFiles( lpSearch, fnRoot, mask, dw-1 );
strcpy( chDest, chPath[0] );
strcpy( chSrc, chPath[dw] );
CopyThem( fnRoot, chDest, chSrc, dw, mask, TRUE );
strcpy( chDest, chPath[0] );
strcpy( chSrc, chPath[dw] );
do {
dwCount = CopyThem( fnRoot, chDest, chSrc, dw, mask, FALSE );
} while ( dwCount != 0 && !EverybodyBailOut);
EverybodyBailOut = TRUE;
return( 0 );
}
VOID
EnumStraglers(
LPSTR lpPath,
FILENAME *fn,
DWORD dwTotalMask
)
{
FILENAME *fnChild;
CHAR NewName[MAX_PATH];
CHAR *pch;
CHAR *pchSpot;
pchSpot = lpPath + strlen(lpPath);
fnChild = fn->fnChild.mem_ptr;
while ( fnChild ) {
pch = pchSpot;
strcpy( pch, fnChild->cFileName );
if ( (fnChild->dwStatus & dwTotalMask) != dwTotalMask ) {
if ( fLogTreeDifferences ) {
printf( "File %s is not on all source locations\n", lpPath );
}
nStraglers++;
}
if ( (fnChild->dwStatus & DIRECTORY) == DIRECTORY ) {
strcat( pch, "\\" );
EnumStraglers( lpPath, fnChild, dwTotalMask );
}
fnChild = fnChild->fnSibling.mem_ptr;
}
}
VOID
DumpStraglers(
DWORD dwTotalMask
)
{
CHAR cPath[MAX_PATH];
strcpy( cPath, "<SRC>\\" );
EnumStraglers( cPath, fnRoot, dwTotalMask );
if ( nStraglers != 0 ) {
printf("Files found on some source locations, but not on others\n");
printf("Run SUCK with -x option to enumerate differences.\n");
}
}
void
EnumDATFileData(
FILENAME *fnParent,
DWORD dwDiskPtr
)
{
FILENAME fnDiskName;
FILENAME *fnChild = &fnDiskName;
FILENAME *fnCurrent;
int iSeek;
int iCount;
//
// Read in this level from the DAT file
//
while ( dwDiskPtr != 0 ) {
// Seek to this entry
iSeek = fseek( SuckDATFile, dwDiskPtr, SEEK_SET );
if ( iSeek != 0 ) {
printf("SUCK.DAT seek error, remove and restart\n");
exit(3);
}
// Read in this entry
iCount = fread( (void *)fnChild, sizeof(FILENAME), 1, SuckDATFile );
if ( iCount != 1 ) {
printf("SUCK.DAT read error, remove and restart\n");
exit(4);
}
#ifdef SEE_EM
printf("Reading record [%s], at %08lX Child %08lX Sib %08lX\n", fnChild->cFileName, dwDiskPtr, fnChild->fnChild.disk_ptr, fnChild->fnSibling.disk_ptr );
printf("Size = %d\n", fnChild->dwFileSizeLow );
#endif
//
// Add this file node to the tree
//
fnCurrent = AllocateFileName();
fnCurrent->dwStatus = fnChild->dwStatus;
fnCurrent->dwCopy = 0;
fnCurrent->fnParent.mem_ptr = fnParent;
fnCurrent->fnChild.mem_ptr = NULL;
fnCurrent->fnSibling.mem_ptr = fnParent->fnChild.mem_ptr;
fnCurrent->dwFileSizeLow = 0;
fnCurrent->dwFileSizeHigh = 0;
fnCurrent->ftFileTime = ftZero;
fnCurrent->dwDATFileSizeLow = fnChild->dwFileSizeLow;
fnCurrent->dwDATFileSizeHigh = fnChild->dwFileSizeHigh;
fnCurrent->ftDATFileTime = fnChild->ftFileTime;
fnCurrent->dwFileNameLen = fnChild->dwFileNameLen;
strcpy( fnCurrent->cFileName, fnChild->cFileName );
fnParent->fnChild.mem_ptr = fnCurrent;
if ( (fnCurrent->dwStatus & DIRECTORY) == DIRECTORY ) {
nDirectories++;
//
// Load this directories children
//
EnumDATFileData( fnCurrent, fnChild->fnChild.disk_ptr );
} else {
fnCurrent->dwStatus = 0;
nFiles++;
}
// Move to next sibling at this level
dwDiskPtr = fnChild->fnSibling.disk_ptr;
}
}
void
LoadFileTimesAndSizes(
BOOL fUseSuckDATFile
)
{
CHAR cPath[MAX_PATH];
FILEHEADER fileheader;
int iCount;
//
// Initialize the tree root
//
fnRoot = AllocateFileName();
fnRoot->fnParent.mem_ptr = NULL;
fnRoot->fnChild.mem_ptr = NULL;
fnRoot->fnSibling.mem_ptr = NULL;
strcpy( fnRoot->cFileName, "<ROOT>" );
// Look for SUCK.DAT
if ( fUseSuckDATFile ) {
SuckDATFile = fopen( SUCK_DAT_FILE, "rb" );
} else {
SuckDATFile = NULL;
}
if ( SuckDATFile != NULL ) {
//
// If file exists, then load the data from it.
//
printf("Loading Previous Statistics...\n");
iCount = fread( &fileheader, sizeof(fileheader), 1, SuckDATFile );
if ( iCount != 1 ) {
printf("Error reading SUCK.DAT file, remove and restart\n");
exit(1);
}
EnumDATFileData( fnRoot, fileheader.fnRoot.disk_ptr );
fclose( SuckDATFile );
}
}
int
EnumFileTimesAndSizes(
DWORD dwDiskPtr,
FILENAME *fn
)
{
FILENAME fnDiskName;
FILENAME *fnChild = &fnDiskName;
FILENAME *fnCurrent;
VIRTPTR fnChildPtr;
VIRTPTR fnSiblingPtr;
int nRecords;
int nChildren;
int iCount;
//
// The 1st guy in the list will be at the end of the list
//
fnSiblingPtr.disk_ptr = 0;
nRecords = 0;
fnCurrent = fn->fnChild.mem_ptr;
while ( fnCurrent ) {
*fnChild = *fnCurrent;
if ( (fnCurrent->dwStatus & DIRECTORY) == DIRECTORY ) {
nChildren = EnumFileTimesAndSizes( dwDiskPtr, fnCurrent );
nRecords += nChildren;
dwDiskPtr += nChildren * sizeof(FILENAME);
if ( nRecords == 0 ) {
fnChildPtr.disk_ptr = 0;
} else {
// Point to previous one, it was our child
fnChildPtr.disk_ptr = dwDiskPtr - sizeof(FILENAME);
}
} else {
fnChildPtr.disk_ptr = 0;
}
fnChild->fnChild.disk_ptr = fnChildPtr.disk_ptr;
fnChild->fnSibling.disk_ptr = fnSiblingPtr.disk_ptr;
fnSiblingPtr.disk_ptr = dwDiskPtr;
#ifdef SEE_EM
printf("Writing record [%s], at %08lX Child %08lX Sib %08lX\n", fnChild->cFileName, dwDiskPtr, fnChild->fnChild.disk_ptr, fnChild->fnSibling.disk_ptr );
printf("Size = %d\n", fnChild->dwFileSizeLow );
#endif
iCount = fwrite( fnChild, sizeof(FILENAME), 1, SuckDATFile );
if ( iCount != 1 ) {
printf("SUCK.DAT error writing data\n");
exit(1);
}
dwDiskPtr += sizeof(FILENAME);
nRecords++;
fnCurrent = fnCurrent->fnSibling.mem_ptr;
}
return( nRecords );
}
VOID
UpdateFileTimesAndSizes(
VOID
)
{
CHAR cPath[MAX_PATH];
FILEHEADER fileheader;
int iSeek;
int iCount;
DWORD dwDiskPtr;
int nChildren;
printf("Updating Statistics...\n");
SuckDATFile = fopen( SUCK_DAT_FILE, "wb+" );
if ( SuckDATFile == NULL ) {
printf( "Error creating file '%s', update aborted\n", SUCK_DAT_FILE );
return;
}
fileheader.fnRoot.disk_ptr = 0; // Temporary...
iCount = fwrite( &fileheader, sizeof(fileheader), 1, SuckDATFile );
if ( iCount != 1 ) {
printf("SUCK.DAT error writing header\n");
exit(1);
}
dwDiskPtr = sizeof(fileheader);
nChildren = EnumFileTimesAndSizes( dwDiskPtr, fnRoot );
dwDiskPtr += nChildren * sizeof(FILENAME);
if ( nChildren == 0 ) {
dwDiskPtr = 0;
} else {
dwDiskPtr -= sizeof(FILENAME);
}
fileheader.fnRoot.disk_ptr = dwDiskPtr; // Now update for real...
iSeek = fseek( SuckDATFile, 0, SEEK_SET );
if ( iSeek != 0 ) {
printf("SUCK.DAT error seeking to write header\n");
exit(3);
}
iCount = fwrite( &fileheader, sizeof(fileheader), 1, SuckDATFile );
if ( iCount != 1 ) {
printf("SUCK.DAT error writing header\n");
exit(1);
}
fclose( SuckDATFile );
}
CHAR *NewArgv[MAX_ARGS];
CHAR chCommand[MAX_COMMAND_LINE+1];
VOID
LookForLastCommand(
INT *pargc,
CHAR **pargv[]
)
{
CHAR *pSpace;
CHAR *pNextArg;
GetPrivateProfileString("Init", "LastCommand", "", chCommand, MAX_COMMAND_LINE, SUCK_INI_FILE );
if ( strlen(chCommand) == 0 ) {
return;
}
pNextArg = chCommand;
*pargv = NewArgv;
(*pargv)[1] = "";
*pargc = 1;
do {
(*pargc)++;
pSpace = strchr( pNextArg, ' ' );
if ( pSpace ) {
*pSpace = '\0';
}
(*pargv)[(*pargc)-1] = pNextArg;
pNextArg = pSpace + 1;
} while ( pSpace != NULL );
}
VOID
UpdateLastCommandLine(
INT argc,
CHAR *argv[]
)
{
CHAR chLastCommand[MAX_COMMAND_LINE+1];
INT nArg;
chLastCommand[0] = '\0';
nArg = 1;
while ( nArg < argc ) {
strcat( chLastCommand, argv[nArg] );
nArg++;
if ( nArg != argc ) {
strcat( chLastCommand, " " );
}
}
WritePrivateProfileString("Init", "LastCommand", chLastCommand, SUCK_INI_FILE );
}
VOID
ReplaceEnvironmentStrings(
CHAR *pText
)
{
CHAR *pOpenPercent;
CHAR *pClosePercent;
CHAR chBuffer[MAX_PATH];
CHAR *pSrc;
CHAR *pEnvString;
chBuffer[0] = '\0';
pSrc = pText;
do {
pOpenPercent = strchr( pSrc, '%' );
if ( pOpenPercent == NULL ) {
strcat( chBuffer, pSrc );
break;
}
pEnvString = pOpenPercent + 1;
pClosePercent = strchr( pEnvString, '%' );
if ( pClosePercent == NULL ) {
strcat( chBuffer, pSrc );
break;
}
if ( pEnvString == pClosePercent ) {
strcat( chBuffer, "%" );
} else {
*pOpenPercent = '\0';
*pClosePercent = '\0';
strcat( chBuffer, pSrc );
GetEnvironmentVariable( pEnvString,
chBuffer + strlen(chBuffer),
MAX_PATH );
}
pSrc = pClosePercent+1;
} while ( TRUE );
strcpy( pText, chBuffer );
}
DWORD
DiffTimes(
SYSTEMTIME *start,
SYSTEMTIME *end
)
{
DWORD nSecStart;
DWORD nSecEnd;
nSecStart = start->wHour*60*60 +
start->wMinute*60 +
start->wSecond;
nSecEnd = end->wHour*60*60 +
end->wMinute*60 +
end->wSecond;
return nSecEnd - nSecStart;
}
VOID
Usage(
VOID
)
{
fputs("SUCK: Usage suck [-options] <dest> <src> [<src>...]\n"
" (maximum of 29 src directories)\n"
"\n"
" where options are: x - List source differences (if any)\n"
" s - Produce script, don't copy\n"
" q - Quiet mode (no stdout)\n"
" p - Display progress on one line\n"
" (cannot be used with -q or -s)\n"
" z - Copy over readonly files\n"
" e - Exclude directory\n"
" e.g. -eidw -emstools\n"
, stderr);
}
VOID
ArgError(
INT nArg,
CHAR * pszArg
)
{
fprintf( stderr, "\nError in arg #%d - '%s'\n\n", nArg, pszArg );
Usage();
exit(1);
}
int
__cdecl
main(
int argc,
char *argv[]
)
{
HANDLE hThreads[MAX_THREADS];
DWORD dwThreadId;
DWORD nThreads;
INT nArg;
DWORD nPaths;
CHAR *pch;
CHAR *pch2;
DWORD dwTotalMask;
OFSTRUCT ofs;
BOOL fUpdateCommandLine = TRUE;
CHAR * pchNextExclude = gExcludes;
SYSTEMTIME stStart;
SYSTEMTIME stEnd;
DWORD nSeconds;
DWORD nMinutes;
// kenhia 15-Mar-1996: add support for -#:<share>
CHAR * PlatformPoundArray[] = PLATFORM_SPECIFIC_SHARES;
DWORD nPound = 0;
// DavidP 23-Jan-1998: Allow multiple levels of quiet
DWORD dwConsoleMode = 0;
BOOL fWasConsoleModeSet = FALSE;
HANDLE hStdOut = NULL;
if ( argc < 2 ) {
LookForLastCommand( &argc, &argv );
fUpdateCommandLine = FALSE;
}
if ( argc < 3 ) {
Usage();
exit(1);
}
nArg = 1;
nPaths = 0;
while ( nArg < argc ) {
pch = argv[nArg];
if ( *pch == '-' ) {
BOOL fExitSwitchLoop = FALSE;
pch++;
while ( *pch && !fExitSwitchLoop) {
switch ( *pch ) {
case 's':
// DavidP 23-Jan-1998: Allow multiple levels of quiet
if ( fProgressMode ) {
ArgError( nArg, argv[nArg] );
}
fScriptMode = TRUE;
break;
case 'q':
// DavidP 23-Jan-1998: Allow multiple levels of quiet
if ( fProgressMode ) {
ArgError( nArg, argv[nArg] );
}
fQuietMode = TRUE;
break;
case 'p': // DavidP 23-Jan-1998: Allow multiple levels of quiet
if ( fQuietMode || fScriptMode ) {
ArgError( nArg, argv[nArg] );
}
fProgressMode = TRUE;
chLineEnd = '\r';
break;
case 'x':
fLogTreeDifferences = TRUE;
break;
case 'y':
fUpdateINIBase = TRUE;
break;
case 'z':
fDestroy = TRUE;
break;
case 'e':
if ( pchNextExclude - gExcludes + strlen(++pch) + 2 > MAX_EXCLUDES ) {
ArgError( nArg, argv[nArg] );
}
strcpy(pchNextExclude, pch);
pchNextExclude += strlen(pchNextExclude)+1;
*pchNextExclude = 0;
fExitSwitchLoop = TRUE;
break;
// kenhia 15-Mar-1996: add support for -#:<share>
case '#':
pch++;
if ( *pch != ':' ) {
Usage();
exit(1);
}
while ( PlatformPoundArray[nPound] ) {
if ( nPaths >= MAX_THREADS ) {
Usage();
exit(1);
}
strcpy( chPath[nPaths], "\\\\" );
strcat( chPath[nPaths], PlatformPoundArray[nPound] );
strcat( chPath[nPaths], "\\" );
strcat( chPath[nPaths], pch+1 );
pch2 = chPath[nPaths] + strlen(chPath[nPaths]) - 1;
if ( *pch2 != '\\' && *pch2 != '/' && *pch2 != ':' ) {
*++pch2 = '\\';
*++pch2 = '\0';
}
ReplaceEnvironmentStrings( chPath[nPaths] );
nPound++;
nPaths++;
}
fExitSwitchLoop = TRUE;
break;
default:
Usage();
exit(1);
}
pch++;
}
} else {
if ( nPaths >= MAX_THREADS ) {
Usage();
exit(1);
}
strcpy( chPath[nPaths], argv[nArg] );
pch = chPath[nPaths] + strlen(chPath[nPaths]) - 1;
if ( *pch != '\\' && *pch != '/' && *pch != ':' ) {
*++pch = '\\';
*++pch = '\0';
}
ReplaceEnvironmentStrings( chPath[nPaths] );
nPaths++;
}
nArg++;
}
nThreads = --nPaths;
if ( nThreads == 0 ) {
Usage();
exit(1);
}
// DavidP 23-Jan-1998: Allow multiple levels of quiet
if ( fProgressMode ) {
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if ( hStdOut != INVALID_HANDLE_VALUE ) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if ( GetConsoleScreenBufferInfo( hStdOut, &csbi ) ) {
nConsoleWidth = csbi.dwSize.X - 1;
}
}
fWasConsoleModeSet = GetConsoleMode( hStdOut, &dwConsoleMode );
if ( fWasConsoleModeSet ) {
SetConsoleMode( hStdOut, dwConsoleMode & ~ENABLE_WRAP_AT_EOL_OUTPUT );
}
}
InitializeCriticalSection( &cs );
InitializeCriticalSection( &pcs );
printf("Streamlined Utility for Copying Kernel v1.1 (%d %s)\n", nThreads, (nThreads == 1 ? "thread" : "threads") );
GetSystemTime( &stStart );
LoadFileTimesAndSizes( fUseDAT );
dwTotalMask = 0;
while ( nPaths ) {
hThreads[nPaths-1] = CreateThread( NULL,
0L,
ThreadFunction,
(LPVOID)UlongToPtr(nPaths),
0,
&dwThreadId );
dwTotalMask |= dwMasks[nPaths-1];
--nPaths;
}
WaitForMultipleObjects( nThreads,
hThreads,
TRUE, // WaitAll
(DWORD)-1 );
// DavidP 23-Jan-1998: Allow multiple levels of quiet
if ( fProgressMode ) {
printf("%*s\r", nConsoleWidth, "");
if ( fWasConsoleModeSet ) {
SetConsoleMode( hStdOut, dwConsoleMode );
}
}
printf("Copy complete, %ld file entries\n", nFiles+nDirectories);
nPaths = 0;
while ( nPaths < nThreads ) {
printf("%11ld bytes from %s\n", dwTotalSizes[nPaths], chPath[nPaths+1] );
nPaths++;
}
nPaths = nThreads;
while ( nPaths ) {
nPaths--;
CloseHandle( hThreads[nPaths] );
}
DumpStraglers( dwTotalMask );
if ( fUpdateINIBase ) {
UpdateFileTimesAndSizes();
}
FreeFileNames();
DeleteCriticalSection( &cs );
DeleteCriticalSection( &pcs );
if ( fUpdateCommandLine ) {
UpdateLastCommandLine( argc, argv );
}
GetSystemTime( &stEnd );
nSeconds = DiffTimes( &stStart, &stEnd );
nMinutes = nSeconds / 60;
nSeconds = nSeconds % 60;
printf("Done, Elapsed time: %02d:%02d\n", nMinutes, nSeconds);
return( 0 );
}