961 lines
21 KiB
C++
961 lines
21 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Replace
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Replace utility
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Ramon Juan San Andres (ramonsa) 01-May-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ulib.hxx"
|
|||
|
#include "arrayit.hxx"
|
|||
|
#include "dir.hxx"
|
|||
|
#include "filter.hxx"
|
|||
|
#include "file.hxx"
|
|||
|
#include "fsnode.hxx"
|
|||
|
#include "stream.hxx"
|
|||
|
#include "substrng.hxx"
|
|||
|
#include "system.hxx"
|
|||
|
#include "replace.hxx"
|
|||
|
|
|||
|
//
|
|||
|
// Pattern that matches all files in a directory
|
|||
|
//
|
|||
|
#define MATCH_ALL_FILES "*.*"
|
|||
|
|
|||
|
|
|||
|
#define CTRL_C (WCHAR)3
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Size of buffers to hold path strings
|
|||
|
//
|
|||
|
#define INITIAL_PATHSTRING_BUFFER_SIZE MAX_PATH
|
|||
|
|
|||
|
|
|||
|
VOID __cdecl
|
|||
|
main (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Main function of the Replace utility
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Initialize stuff
|
|||
|
//
|
|||
|
DEFINE_CLASS_DESCRIPTOR( REPLACE );
|
|||
|
|
|||
|
//
|
|||
|
// Now do the replacement
|
|||
|
//
|
|||
|
{
|
|||
|
REPLACE Replace;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the Replace object.
|
|||
|
//
|
|||
|
Replace.Initialize();
|
|||
|
|
|||
|
//
|
|||
|
// Do our thing
|
|||
|
//
|
|||
|
Replace.DoReplace();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DEFINE_CONSTRUCTOR( REPLACE, PROGRAM );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::Initialize (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the REPLACE object
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if initialized, FALSE otherwise
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Initialize program object
|
|||
|
//
|
|||
|
PROGRAM::Initialize( REPLACE_MESSAGE_USAGE );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate global structures and initialize them
|
|||
|
//
|
|||
|
InitializeThings();
|
|||
|
|
|||
|
//
|
|||
|
// Parse the arguments
|
|||
|
//
|
|||
|
SetArguments();
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
REPLACE::~REPLACE (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Destructs a REPLACE object
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Deallocate the global structures previously allocated
|
|||
|
//
|
|||
|
DeallocateThings();
|
|||
|
|
|||
|
//
|
|||
|
// Exit without error
|
|||
|
//
|
|||
|
DisplayMessageAndExit( 0, NULL, EXIT_NORMAL );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
REPLACE::InitializeThings (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the global variables that need initialization
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the path string buffers
|
|||
|
//
|
|||
|
_PathString1 = (LPWSTR)MALLOC( INITIAL_PATHSTRING_BUFFER_SIZE );
|
|||
|
_PathString2 = (LPWSTR)MALLOC( INITIAL_PATHSTRING_BUFFER_SIZE );
|
|||
|
|
|||
|
_PathString1Size = _PathString2Size = INITIAL_PATHSTRING_BUFFER_SIZE;
|
|||
|
|
|||
|
_Keyboard = NEW KEYBOARD;
|
|||
|
|
|||
|
if ( !_PathString1 || !_PathString2 || !_Keyboard ) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// initialize the keyboard and set ctrl-c handling
|
|||
|
//
|
|||
|
_Keyboard->Initialize();
|
|||
|
_Keyboard->EnableBreakHandling();
|
|||
|
|
|||
|
//
|
|||
|
// Initialize our data
|
|||
|
//
|
|||
|
_SourcePath = NULL;
|
|||
|
_DestinationPath = NULL;
|
|||
|
_FilesAdded = 0;
|
|||
|
_FilesReplaced = 0;
|
|||
|
_SourceDirectory = NULL;
|
|||
|
_Pattern = NULL;
|
|||
|
_FilesInSrc = NULL;
|
|||
|
|
|||
|
_AddSwitch = FALSE; // use by DisplayMessageAndExit before any
|
|||
|
// of those boolean _*Switch is being initialized
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
REPLACE::DeallocateThings (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Deallocates the stuff that was initialized in InitializeThings()
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
DELETE( _FilesInSrc );
|
|||
|
DELETE( _SourceDirectory );
|
|||
|
DELETE( _Pattern );
|
|||
|
DELETE( _Keyboard );
|
|||
|
|
|||
|
FREE( _PathString1 );
|
|||
|
FREE( _PathString2 );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::DoReplace (
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the function that performs the Replace.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PFSN_DIRECTORY DestinationDirectory;
|
|||
|
FSN_FILTER Filter;
|
|||
|
WCHAR Char;
|
|||
|
|
|||
|
//
|
|||
|
// Get the source directory object and the pattern that we will use
|
|||
|
// for file matching.
|
|||
|
//
|
|||
|
GetDirectoryAndPattern( _SourcePath, &_SourceDirectory, &_Pattern );
|
|||
|
|
|||
|
DebugPtrAssert( _SourceDirectory );
|
|||
|
DebugPtrAssert( _Pattern );
|
|||
|
|
|||
|
//
|
|||
|
// Get the destination directory
|
|||
|
//
|
|||
|
GetDirectory( _DestinationPath, &DestinationDirectory );
|
|||
|
|
|||
|
DebugPtrAssert( DestinationDirectory );
|
|||
|
|
|||
|
//
|
|||
|
// Wait if requested
|
|||
|
//
|
|||
|
if ( _WaitSwitch ) {
|
|||
|
|
|||
|
DisplayMessage( REPLACE_MESSAGE_PRESS_ANY_KEY );
|
|||
|
AbortIfCtrlC();
|
|||
|
|
|||
|
//
|
|||
|
// All input is in raw mode.
|
|||
|
//
|
|||
|
_Keyboard->DisableLineMode();
|
|||
|
GetStandardInput()->ReadChar( &Char );
|
|||
|
_Keyboard->EnableLineMode();
|
|||
|
|
|||
|
GetStandardOutput()->WriteChar( Char );
|
|||
|
GetStandardOutput()->WriteChar( (WCHAR)'\r');
|
|||
|
GetStandardOutput()->WriteChar( (WCHAR)'\n');
|
|||
|
|
|||
|
//
|
|||
|
// Check for ctrl-c
|
|||
|
//
|
|||
|
if ( Char == CTRL_C ) {
|
|||
|
exit ( EXIT_PATH_NOT_FOUND );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get an array containing all the files in the source directory
|
|||
|
// that match the pattern.
|
|||
|
//
|
|||
|
// This is so that Replacer() does not have to get the same
|
|||
|
// information over and over when the Subdir switch is set.
|
|||
|
//
|
|||
|
_FilesInSrc = GetFileArray( _SourceDirectory, _Pattern );
|
|||
|
DebugPtrAssert( _FilesInSrc );
|
|||
|
|
|||
|
if ( _SubdirSwitch ) {
|
|||
|
|
|||
|
//
|
|||
|
// First, replace the files in the directory specified
|
|||
|
//
|
|||
|
Replacer( this, DestinationDirectory, NULL );
|
|||
|
|
|||
|
Filter.Initialize();
|
|||
|
Filter.SetAttributes( (FSN_ATTRIBUTE)FILE_ATTRIBUTE_DIRECTORY );
|
|||
|
|
|||
|
//
|
|||
|
// Now traverse the destination directory, calling the
|
|||
|
// replacer function for each subdirectory.
|
|||
|
//
|
|||
|
DestinationDirectory->Traverse( this,
|
|||
|
&Filter,
|
|||
|
NULL,
|
|||
|
REPLACE::Replacer );
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Call the replace function, which takes care of replacements
|
|||
|
//
|
|||
|
Replacer(this, DestinationDirectory, NULL );
|
|||
|
}
|
|||
|
DELETE( DestinationDirectory );
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
REPLACE::GetDirectoryAndPattern(
|
|||
|
IN PPATH Path,
|
|||
|
OUT PFSN_DIRECTORY *Directory,
|
|||
|
OUT PWSTRING *Pattern
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Given a path, this function obtains a directory object and a pattern.
|
|||
|
|
|||
|
Normally, the pattern is the filename portion of the path, but if the
|
|||
|
entire path refers to a directory, then the pattern is "*.*"
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Path - Supplies pointer to path
|
|||
|
Directory - Supplies pointer to pointer to directory
|
|||
|
Pattern - Supplies pointer to pointer to pattern
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PATH TmpPath;
|
|||
|
PWSTRING Name;
|
|||
|
PFSN_DIRECTORY Dir;
|
|||
|
PWSTRING Ptrn;
|
|||
|
|
|||
|
DebugAssert( Path );
|
|||
|
DebugAssert( Directory );
|
|||
|
DebugAssert( Pattern );
|
|||
|
|
|||
|
//
|
|||
|
// If the name passed is a directory, it is an error.
|
|||
|
// Otherwise, split the path into Directory and Pattern
|
|||
|
// portions.
|
|||
|
//
|
|||
|
Dir = SYSTEM::QueryDirectory( Path );
|
|||
|
|
|||
|
if ( Dir ||
|
|||
|
(Name = Path->QueryName()) == NULL ||
|
|||
|
(Ptrn = Name->QueryString()) == NULL ) {
|
|||
|
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_FILES_FOUND,
|
|||
|
Path->GetPathString(),
|
|||
|
EXIT_FILE_NOT_FOUND );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// We're finished with Name.
|
|||
|
//
|
|||
|
DELETE( Name );
|
|||
|
|
|||
|
//
|
|||
|
// Get the directory
|
|||
|
//
|
|||
|
TmpPath.Initialize( Path, TRUE );
|
|||
|
TmpPath.TruncateBase();
|
|||
|
|
|||
|
Dir = SYSTEM::QueryDirectory( &TmpPath );
|
|||
|
|
|||
|
if ( !Dir ) {
|
|||
|
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_PATH_NOT_FOUND,
|
|||
|
Path->GetPathString(),
|
|||
|
EXIT_PATH_NOT_FOUND );
|
|||
|
}
|
|||
|
|
|||
|
*Directory = Dir;
|
|||
|
*Pattern = Ptrn;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
REPLACE::GetDirectory(
|
|||
|
IN PCPATH Path,
|
|||
|
OUT PFSN_DIRECTORY *Directory
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Makes a directory out of a path.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Path - Supplies pointer to path
|
|||
|
Directory - Supplies pointer to pointer to directory
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFSN_DIRECTORY Dir;
|
|||
|
|
|||
|
|
|||
|
if ( !(Dir = SYSTEM::QueryDirectory( Path )) ) {
|
|||
|
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_PATH_NOT_FOUND,
|
|||
|
Path->GetPathString(),
|
|||
|
EXIT_PATH_NOT_FOUND );
|
|||
|
}
|
|||
|
|
|||
|
*Directory = Dir;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PARRAY
|
|||
|
REPLACE::GetFileArray(
|
|||
|
IN PFSN_DIRECTORY Directory,
|
|||
|
IN PWSTRING Pattern
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Gets an array of those files in a directory matching a pattern.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Directory - Supplies pointer to directory
|
|||
|
Pattern - Supplies pointer to pattern
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Pointer to the array of files
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
PARRAY Array;
|
|||
|
FSN_FILTER Filter;
|
|||
|
|
|||
|
DebugPtrAssert( Directory );
|
|||
|
DebugPtrAssert( Pattern );
|
|||
|
|
|||
|
Filter.Initialize();
|
|||
|
Filter.SetFileName( Pattern );
|
|||
|
Filter.SetAttributes( (FSN_ATTRIBUTE)0, (FSN_ATTRIBUTE)0, (FSN_ATTRIBUTE)FILE_ATTRIBUTE_DIRECTORY );
|
|||
|
|
|||
|
Array = Directory->QueryFsnodeArray( &Filter );
|
|||
|
|
|||
|
DebugPtrAssert( Array );
|
|||
|
|
|||
|
return Array;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::Replacer (
|
|||
|
IN PVOID This,
|
|||
|
IN OUT PFSNODE DirectoryNode,
|
|||
|
IN PPATH DummyPath
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the heart of Replace. Given a destination directory, it
|
|||
|
performs the replacement/additions according to the global switches
|
|||
|
and the SourceDirectory and Pattern.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
This - Supplies pointer to the REPLACE object
|
|||
|
Node - Supplies pointer to the directory node.
|
|||
|
DummyPath - Required by FSN_DIRECTORY::Traverse(), must be
|
|||
|
NULL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if operation successful.
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
DebugAssert( DummyPath == NULL );
|
|||
|
DebugAssert( DirectoryNode->IsDirectory() );
|
|||
|
|
|||
|
((PREPLACE)This)->AbortIfCtrlC();
|
|||
|
|
|||
|
if ( ((PREPLACE)This)->_AddSwitch ) {
|
|||
|
return ((PREPLACE)This)->AddFiles( (PFSN_DIRECTORY)DirectoryNode );
|
|||
|
} else {
|
|||
|
return ((PREPLACE)This)->ReplaceFiles( (PFSN_DIRECTORY)DirectoryNode );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::AddFiles (
|
|||
|
IN OUT PFSN_DIRECTORY DestinationDirectory
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Adds those files from the SourceDirectory that match Pattern to the
|
|||
|
DestinationDirectory. The array of files is already in the
|
|||
|
FilesInSrc array.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DestinationDirectory - Supplies pointer to destination
|
|||
|
directory.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if operation successful.
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PARRAY_ITERATOR Iterator;
|
|||
|
PFSN_FILE File;
|
|||
|
PFSN_FILE FileToCreate;
|
|||
|
PATH DestinationPath;
|
|||
|
PWSTRING Name;
|
|||
|
|
|||
|
DebugPtrAssert( DestinationDirectory );
|
|||
|
DebugPtrAssert( _FilesInSrc );
|
|||
|
|
|||
|
//
|
|||
|
// Get destination path
|
|||
|
//
|
|||
|
DestinationPath.Initialize( DestinationDirectory->GetPath() );
|
|||
|
|
|||
|
//
|
|||
|
// Obtain an iterator for going thru the files
|
|||
|
//
|
|||
|
Iterator = ( PARRAY_ITERATOR )_FilesInSrc->QueryIterator( );
|
|||
|
if (Iterator == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For each file in the array, see if it exists in the destination
|
|||
|
// directory, and if it does not, then copy it.
|
|||
|
//
|
|||
|
while ( File = (PFSN_FILE)Iterator->GetNext() ) {
|
|||
|
|
|||
|
DebugAssert( !(((PFSNODE)File)->IsDirectory()) );
|
|||
|
|
|||
|
Name = File->QueryName();
|
|||
|
if (Name == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Form the path in the target file
|
|||
|
//
|
|||
|
DestinationPath.AppendBase( Name );
|
|||
|
|
|||
|
DELETE( Name );
|
|||
|
|
|||
|
//
|
|||
|
// See if the file exists
|
|||
|
//
|
|||
|
FileToCreate = SYSTEM::QueryFile( &DestinationPath );
|
|||
|
|
|||
|
//
|
|||
|
// If the file does not exist, then it has to be added
|
|||
|
//
|
|||
|
if ( !FileToCreate ) {
|
|||
|
|
|||
|
if ( !_PromptSwitch || Prompt( REPLACE_MESSAGE_ADD_YES_NO, &DestinationPath ) ) {
|
|||
|
|
|||
|
DisplayMessage( REPLACE_MESSAGE_ADDING, NORMAL_MESSAGE, "%W", DestinationPath.GetPathString() );
|
|||
|
|
|||
|
CopyTheFile( File->GetPath(), &DestinationPath );
|
|||
|
|
|||
|
_FilesAdded++;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DELETE( FileToCreate );
|
|||
|
|
|||
|
//
|
|||
|
// Set the destination path back to what it originally was
|
|||
|
// ( i.e. directory specification, no file ).
|
|||
|
//
|
|||
|
DestinationPath.TruncateBase();
|
|||
|
|
|||
|
}
|
|||
|
DELETE( Iterator );
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::ReplaceFiles (
|
|||
|
IN OUT PFSN_DIRECTORY DestinationDirectory
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Replaces those files in the DestinationDirectory that match Pattern
|
|||
|
by the corresponding files in SourceDirectory.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DestinationDirectory - Supplies pointer to destination
|
|||
|
directory.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if operation successful.
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PARRAY_ITERATOR Iterator;
|
|||
|
PFSN_FILE File;
|
|||
|
PFSN_FILE FileToReplace;
|
|||
|
PATH DestinationPath;
|
|||
|
PWSTRING Name;
|
|||
|
PTIMEINFO TimeSrc;
|
|||
|
PTIMEINFO TimeDst;
|
|||
|
BOOLEAN Proceed = TRUE;
|
|||
|
|
|||
|
DebugPtrAssert( DestinationDirectory );
|
|||
|
DebugPtrAssert( _FilesInSrc );
|
|||
|
|
|||
|
//
|
|||
|
// Get destination path
|
|||
|
//
|
|||
|
DestinationPath.Initialize( DestinationDirectory->GetPath() );
|
|||
|
|
|||
|
//
|
|||
|
// Obtain an iterator for going thru the files
|
|||
|
//
|
|||
|
Iterator = ( PARRAY_ITERATOR )_FilesInSrc->QueryIterator( );
|
|||
|
if (Iterator == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For each file in the array, see if it exists in the destination
|
|||
|
// directory, and if it does, replace it
|
|||
|
//
|
|||
|
while ( File = (PFSN_FILE)Iterator->GetNext() ) {
|
|||
|
|
|||
|
AbortIfCtrlC();
|
|||
|
|
|||
|
DebugAssert( !(((PFSNODE)File)->IsDirectory()) );
|
|||
|
|
|||
|
Name = File->QueryName();
|
|||
|
if (Name == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Form the path in the target file
|
|||
|
//
|
|||
|
DestinationPath.AppendBase( Name );
|
|||
|
|
|||
|
DELETE( Name );
|
|||
|
|
|||
|
//
|
|||
|
// See if the file exists
|
|||
|
//
|
|||
|
FileToReplace = SYSTEM::QueryFile( &DestinationPath );
|
|||
|
|
|||
|
|
|||
|
if ( FileToReplace ) {
|
|||
|
|
|||
|
//
|
|||
|
// If the CompareTime switch is set, then we only proceed if
|
|||
|
// the destination file is older than the source file.
|
|||
|
//
|
|||
|
if ( _CompareTimeSwitch ) {
|
|||
|
|
|||
|
TimeSrc = File->QueryTimeInfo();
|
|||
|
TimeDst = FileToReplace->QueryTimeInfo();
|
|||
|
|
|||
|
if (TimeSrc == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
if (TimeDst == NULL) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
return FALSE; // help lint
|
|||
|
}
|
|||
|
|
|||
|
Proceed = *TimeDst < *TimeSrc;
|
|||
|
|
|||
|
DELETE( TimeSrc );
|
|||
|
DELETE( TimeDst );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( Proceed ) {
|
|||
|
|
|||
|
//
|
|||
|
// We replace the file if it is NOT read-only
|
|||
|
// (unless the ReadOnly switch is set )
|
|||
|
//
|
|||
|
if ( _ReadOnlySwitch || !(FileToReplace->IsReadOnly()) ) {
|
|||
|
|
|||
|
if ( !_PromptSwitch || Prompt( REPLACE_MESSAGE_REPLACE_YES_NO, &DestinationPath ) ) {
|
|||
|
|
|||
|
DisplayMessage( REPLACE_MESSAGE_REPLACING, NORMAL_MESSAGE, "%W", DestinationPath.GetPathString() );
|
|||
|
|
|||
|
//
|
|||
|
// If the file is read-only, we reset the read-only attribute
|
|||
|
// before copying.
|
|||
|
//
|
|||
|
if ( FileToReplace->IsReadOnly() ) {
|
|||
|
FileToReplace->ResetReadOnlyAttribute();
|
|||
|
}
|
|||
|
|
|||
|
CopyTheFile( File->GetPath(), &DestinationPath );
|
|||
|
|
|||
|
_FilesReplaced++;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The file is read-only but the ReadOnly flag was
|
|||
|
// not set, we error out.
|
|||
|
//
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_ACCESS_DENIED,
|
|||
|
DestinationPath.GetPathString(),
|
|||
|
EXIT_ACCESS_DENIED );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DELETE( FileToReplace );
|
|||
|
|
|||
|
//
|
|||
|
// Set the destination path back to what it originally was
|
|||
|
// ( i.e. directory specification, no file name part ).
|
|||
|
//
|
|||
|
DestinationPath.TruncateBase();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DELETE( Iterator );
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::Prompt (
|
|||
|
IN MSGID MessageId,
|
|||
|
IN PCPATH Path
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Gets confirmation from the user about a file to be added/replaced
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MessageId - Supplies the Id of the message to use for prompting
|
|||
|
Path - Supplies path to use as parameter for the message.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if the user confirmed the add/replace
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
DisplayMessage( MessageId, NORMAL_MESSAGE, "%W", Path->GetPathString() );
|
|||
|
return _Message.IsYesResponse();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
REPLACE::CopyTheFile (
|
|||
|
IN PCPATH SrcPath,
|
|||
|
IN PCPATH DstPath
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Copies a file
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SrcPath - Supplies path of source file
|
|||
|
DstFile - Supplies path of destination file
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Size;
|
|||
|
|
|||
|
DebugPtrAssert( SrcPath );
|
|||
|
DebugPtrAssert( DstPath );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the buffers are big enough to hold the
|
|||
|
// paths
|
|||
|
//
|
|||
|
Size = (SrcPath->GetPathString()->QueryChCount() + 1) * 2;
|
|||
|
if ( Size > _PathString1Size ) {
|
|||
|
_PathString1 = (LPWSTR)REALLOC( _PathString1, (unsigned int)Size );
|
|||
|
DebugPtrAssert( _PathString1 );
|
|||
|
_PathString1Size = Size;
|
|||
|
}
|
|||
|
|
|||
|
Size = (DstPath->GetPathString()->QueryChCount() + 1) * 2;
|
|||
|
if ( Size > _PathString2Size ) {
|
|||
|
_PathString2 = (LPWSTR)REALLOC( _PathString2, (unsigned int)Size );
|
|||
|
DebugPtrAssert( _PathString2 );
|
|||
|
_PathString2Size = Size;
|
|||
|
}
|
|||
|
|
|||
|
if ( !_PathString1 || !_PathString2 ) {
|
|||
|
DisplayMessageAndExit( REPLACE_ERROR_NO_MEMORY, NULL, EXIT_NO_MEMORY );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the paths to LPWSTR so that we can call CopyFile()
|
|||
|
//
|
|||
|
SrcPath->GetPathString()->QueryWSTR( 0, TO_END, _PathString1, _PathString1Size/sizeof(WCHAR) );
|
|||
|
DstPath->GetPathString()->QueryWSTR( 0, TO_END, _PathString2, _PathString2Size/sizeof(WCHAR) );
|
|||
|
|
|||
|
//
|
|||
|
// Now do the copy
|
|||
|
//
|
|||
|
if ( !CopyFile( _PathString1, _PathString2, FALSE ) ) {
|
|||
|
|
|||
|
ExitWithError( GetLastError() );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|