You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1451 lines
43 KiB
1451 lines
43 KiB
#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;
|
|
}
|
|
|
|
strncpy( NewName, lpwfd->cFileName, sizeof (NewName) / sizeof (NewName[0]) );
|
|
NewName [(sizeof (NewName) / sizeof (NewName[0])) - 1] = 0;
|
|
|
|
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 );
|
|
}
|