876 lines
26 KiB
C++
876 lines
26 KiB
C++
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Argument
|
||
|
||
Abstract:
|
||
|
||
Argument processing for the XCopy directory copy utility
|
||
|
||
Author:
|
||
|
||
Ramon Juan San Andres (ramonsa) 01-May-1991
|
||
|
||
Notes:
|
||
|
||
The arguments accepted by the XCopy utility are:
|
||
|
||
Source directory.- Source path.
|
||
|
||
Dest. directory.- Destination path.
|
||
|
||
Archive switch.- Copy files that have their archive bit set
|
||
|
||
Date.- Copy files modified on or after the specifiec
|
||
date.
|
||
|
||
Empty switch.- Copy directories even if empty. Subdir switch
|
||
must also be set.
|
||
|
||
Modify switch.- Same as Archive switch, but turns off archive
|
||
bit in the source file after copying.
|
||
|
||
Prompt switch.- Prompts before copying each file.
|
||
|
||
Subdir switch.- Copies also subdirectories, unless they are empty.
|
||
(Empty directories are copied if the Empty switch
|
||
is set).
|
||
|
||
Verify switch.- Verifies each copy.
|
||
|
||
Wait switch.- Wait before starting to copy the files.
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include "ulib.hxx"
|
||
#include "arg.hxx"
|
||
#include "arrayit.hxx"
|
||
#include "dir.hxx"
|
||
#include "xcopy.hxx"
|
||
#include "stringar.hxx"
|
||
#include "file.hxx"
|
||
#include "filestrm.hxx"
|
||
|
||
//
|
||
// Switch characters. Used for maintaining DOS5 compatibility when
|
||
// displaying error messages
|
||
//
|
||
#define SWITCH_CHARACTERS "dDaAeEmMpPsSvVwW?"
|
||
|
||
//
|
||
// Static variables
|
||
//
|
||
|
||
PARRAY LexArray;
|
||
PPATH_ARGUMENT FirstPathArgument = NULL;
|
||
PPATH_ARGUMENT FirstQuotedPathArgument = NULL;
|
||
PPATH_ARGUMENT SecondPathArgument = NULL;
|
||
PPATH_ARGUMENT SecondQuotedPathArgument = NULL;
|
||
PFLAG_ARGUMENT ArchiveArgument = NULL;
|
||
PTIMEINFO_ARGUMENT DateArgument = NULL;
|
||
PFLAG_ARGUMENT EmptyArgument = NULL;
|
||
PFLAG_ARGUMENT ModifyArgument = NULL;
|
||
PFLAG_ARGUMENT PromptArgument = NULL;
|
||
PFLAG_ARGUMENT SubdirArgument = NULL;
|
||
PFLAG_ARGUMENT VerifyArgument = NULL;
|
||
PFLAG_ARGUMENT WaitArgument = NULL;
|
||
PFLAG_ARGUMENT HelpArgument = NULL;
|
||
PFLAG_ARGUMENT ContinueArgument = NULL;
|
||
|
||
PFLAG_ARGUMENT IntelligentArgument = NULL;
|
||
PFLAG_ARGUMENT VerboseArgument = NULL;
|
||
PFLAG_ARGUMENT OldArgument = NULL;
|
||
PFLAG_ARGUMENT HiddenArgument = NULL;
|
||
PFLAG_ARGUMENT ReadOnlyArgument = NULL;
|
||
PFLAG_ARGUMENT SilentArgument = NULL;
|
||
PFLAG_ARGUMENT NoCopyArgument = NULL;
|
||
PFLAG_ARGUMENT StructureArgument = NULL;
|
||
PFLAG_ARGUMENT UpdateArgument = NULL;
|
||
PFLAG_ARGUMENT CopyAttrArgument = NULL;
|
||
PFLAG_ARGUMENT UseShortArgument = NULL;
|
||
PFLAG_ARGUMENT RestartableArgument = NULL;
|
||
|
||
PSTRING_ARGUMENT ExclusionListArgument = NULL;
|
||
|
||
PSTRING_ARGUMENT InvalidSwitchArgument = NULL;
|
||
|
||
|
||
BOOLEAN HelpSwitch;
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
|
||
|
||
VOID
|
||
XCOPY::SetArguments(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Obtains the arguments for the XCopy utility
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PATH_ARGUMENT LocalFirstPathArgument;
|
||
PATH_ARGUMENT LocalFirstQuotedPathArgument;
|
||
PATH_ARGUMENT LocalSecondPathArgument;
|
||
PATH_ARGUMENT LocalSecondQuotedPathArgument;
|
||
FLAG_ARGUMENT LocalArchiveArgument;
|
||
TIMEINFO_ARGUMENT LocalDateArgument;
|
||
FLAG_ARGUMENT LocalOldArgument;
|
||
FLAG_ARGUMENT LocalEmptyArgument;
|
||
FLAG_ARGUMENT LocalModifyArgument;
|
||
FLAG_ARGUMENT LocalPromptArgument;
|
||
FLAG_ARGUMENT LocalSubdirArgument;
|
||
FLAG_ARGUMENT LocalVerifyArgument;
|
||
FLAG_ARGUMENT LocalWaitArgument;
|
||
FLAG_ARGUMENT LocalHelpArgument;
|
||
FLAG_ARGUMENT LocalContinueArgument;
|
||
FLAG_ARGUMENT LocalIntelligentArgument;
|
||
FLAG_ARGUMENT LocalVerboseArgument;
|
||
FLAG_ARGUMENT LocalHiddenArgument;
|
||
FLAG_ARGUMENT LocalReadOnlyArgument;
|
||
FLAG_ARGUMENT LocalSilentArgument;
|
||
FLAG_ARGUMENT LocalNoCopyArgument;
|
||
FLAG_ARGUMENT LocalStructureArgument;
|
||
FLAG_ARGUMENT LocalUpdateArgument;
|
||
FLAG_ARGUMENT LocalCopyAttrArgument;
|
||
FLAG_ARGUMENT LocalUseShortArgument;
|
||
FLAG_ARGUMENT LocalRestartableArgument;
|
||
STRING_ARGUMENT LocalExclusionListArgument;
|
||
|
||
|
||
STRING_ARGUMENT LocalInvalidSwitchArgument;
|
||
ARRAY LocalLexArray;
|
||
|
||
//
|
||
// Set the static global pointers
|
||
//
|
||
FirstPathArgument = &LocalFirstPathArgument;
|
||
FirstQuotedPathArgument = &LocalFirstQuotedPathArgument;
|
||
SecondPathArgument = &LocalSecondPathArgument;
|
||
SecondQuotedPathArgument = &LocalSecondQuotedPathArgument;
|
||
ArchiveArgument = &LocalArchiveArgument;
|
||
DateArgument = &LocalDateArgument;
|
||
OldArgument = &LocalOldArgument;
|
||
EmptyArgument = &LocalEmptyArgument;
|
||
ModifyArgument = &LocalModifyArgument;
|
||
PromptArgument = &LocalPromptArgument;
|
||
SubdirArgument = &LocalSubdirArgument;
|
||
VerifyArgument = &LocalVerifyArgument;
|
||
WaitArgument = &LocalWaitArgument;
|
||
HelpArgument = &LocalHelpArgument;
|
||
ContinueArgument = &LocalContinueArgument;
|
||
IntelligentArgument = &LocalIntelligentArgument;
|
||
VerboseArgument = &LocalVerboseArgument;
|
||
HiddenArgument = &LocalHiddenArgument;
|
||
ReadOnlyArgument = &LocalReadOnlyArgument;
|
||
SilentArgument = &LocalSilentArgument;
|
||
NoCopyArgument = &LocalNoCopyArgument;
|
||
StructureArgument = &LocalStructureArgument;
|
||
UpdateArgument = &LocalUpdateArgument;
|
||
CopyAttrArgument = &LocalCopyAttrArgument;
|
||
UseShortArgument = &LocalUseShortArgument;
|
||
ExclusionListArgument = &LocalExclusionListArgument;
|
||
InvalidSwitchArgument = &LocalInvalidSwitchArgument;
|
||
LexArray = &LocalLexArray;
|
||
RestartableArgument = &LocalRestartableArgument;
|
||
|
||
//
|
||
// Parse the arguments
|
||
//
|
||
GetArgumentsCmd();
|
||
|
||
//
|
||
// Verify the arguments
|
||
//
|
||
CheckArgumentConsistency();
|
||
|
||
LocalLexArray.DeleteAllMembers();
|
||
}
|
||
|
||
|
||
VOID
|
||
GetSourceAndDestinationPath(
|
||
IN OUT PPATH_ARGUMENT FirstPathArgument,
|
||
IN OUT PPATH_ARGUMENT FirstQuotedPathArgument,
|
||
IN OUT PPATH_ARGUMENT SecondPathArgument,
|
||
IN OUT PPATH_ARGUMENT SecondQuotedPathArgument,
|
||
IN OUT PARGUMENT_LEXEMIZER ArgLex,
|
||
OUT PPATH* SourcePath,
|
||
OUT PPATH* DestinationPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the Source and Destination path from
|
||
the given list of arguments.
|
||
|
||
Arguments:
|
||
|
||
FirstPathArgument - Supplies the first unquoted path argument.
|
||
FirstQuotedPathArgument - Supplies the first quoted path argument.
|
||
SecondPathArgument - Supplies the second unquoted path argument.
|
||
SecondQuotedPathArgument - Supplies the second quoted path argument.
|
||
ArgLex - Supplies the argument lexemizer.
|
||
SourcePath - Returns the source path.
|
||
DestinationPath - Returns the destination path.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN f, qf, s, qs;
|
||
PPATH_ARGUMENT source, destination;
|
||
ULONG i;
|
||
PWSTRING string, qstring;
|
||
|
||
f = FirstPathArgument->IsValueSet();
|
||
qf = FirstQuotedPathArgument->IsValueSet();
|
||
s = SecondPathArgument->IsValueSet();
|
||
qs = SecondQuotedPathArgument->IsValueSet();
|
||
source = NULL;
|
||
destination = NULL;
|
||
*SourcePath = NULL;
|
||
*DestinationPath = NULL;
|
||
|
||
if (f && !qf && s && !qs) {
|
||
|
||
source = FirstPathArgument;
|
||
destination = SecondPathArgument;
|
||
|
||
} else if (!f && qf && !s && qs) {
|
||
|
||
source = FirstQuotedPathArgument;
|
||
destination = SecondQuotedPathArgument;
|
||
|
||
} else if (f && qf && !s && !qs) {
|
||
|
||
string = FirstPathArgument->GetLexeme();
|
||
qstring = FirstQuotedPathArgument->GetLexeme();
|
||
|
||
for (i = 0; i < ArgLex->QueryLexemeCount(); i++) {
|
||
if (!ArgLex->GetLexemeAt(i)->Strcmp(string)) {
|
||
source = FirstPathArgument;
|
||
destination = FirstQuotedPathArgument;
|
||
break;
|
||
}
|
||
|
||
if (!ArgLex->GetLexemeAt(i)->Strcmp(qstring)) {
|
||
source = FirstQuotedPathArgument;
|
||
destination = FirstPathArgument;
|
||
break;
|
||
}
|
||
}
|
||
} else if (f && !qf && !s && !qs) {
|
||
source = FirstPathArgument;
|
||
} else if (!f && qf && !s && !qs) {
|
||
source = FirstQuotedPathArgument;
|
||
}
|
||
|
||
if (source) {
|
||
if (!(*SourcePath = NEW PATH) ||
|
||
!(*SourcePath)->Initialize(source->GetPath(),
|
||
VerboseArgument->IsValueSet())) {
|
||
|
||
*SourcePath = NULL;
|
||
}
|
||
}
|
||
|
||
if (destination) {
|
||
if (!(*DestinationPath = NEW PATH) ||
|
||
!(*DestinationPath)->Initialize(destination->GetPath(),
|
||
VerboseArgument->IsValueSet())) {
|
||
|
||
*DestinationPath = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
XCOPY::GetArgumentsCmd(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Obtains the arguments from the Command line
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ARRAY ArgArray;
|
||
PATH_ARGUMENT ProgramNameArgument;
|
||
DSTRING CmdLine;
|
||
DSTRING InvalidParms;
|
||
WCHAR Ch;
|
||
PWSTRING InvalidSwitch;
|
||
PARGUMENT_LEXEMIZER ArgLex;
|
||
|
||
//
|
||
// Prepare for parsing
|
||
//
|
||
if (//
|
||
// Initialize the arguments
|
||
//
|
||
!(CmdLine.Initialize( GetCommandLine() )) ||
|
||
!(ArgArray.Initialize( 15, 15 )) ||
|
||
!(ProgramNameArgument.Initialize( "*" )) ||
|
||
!(FirstPathArgument->Initialize( "*", FALSE )) ||
|
||
!(FirstQuotedPathArgument->Initialize( "\"*\"", FALSE )) ||
|
||
!(SecondPathArgument->Initialize( "*", FALSE)) ||
|
||
!(SecondQuotedPathArgument->Initialize( "\"*\"", FALSE)) ||
|
||
!(ArchiveArgument->Initialize( "/A" )) ||
|
||
!(DateArgument->Initialize( "/D:*" )) ||
|
||
!(OldArgument->Initialize( "/D" )) ||
|
||
!(EmptyArgument->Initialize( "/E" )) ||
|
||
!(ModifyArgument->Initialize( "/M" )) ||
|
||
!(PromptArgument->Initialize( "/P" )) ||
|
||
!(SubdirArgument->Initialize( "/S" )) ||
|
||
!(VerifyArgument->Initialize( "/V" )) ||
|
||
!(WaitArgument->Initialize( "/W" )) ||
|
||
!(HelpArgument->Initialize( "/?" )) ||
|
||
!(ContinueArgument->Initialize( "/C" )) ||
|
||
!(IntelligentArgument->Initialize( "/I" )) ||
|
||
!(VerboseArgument->Initialize( "/F" )) ||
|
||
!(HiddenArgument->Initialize( "/H" )) ||
|
||
!(ReadOnlyArgument->Initialize( "/R" )) ||
|
||
!(SilentArgument->Initialize( "/Q" )) ||
|
||
!(NoCopyArgument->Initialize( "/L" )) ||
|
||
!(StructureArgument->Initialize( "/T" )) ||
|
||
!(UpdateArgument->Initialize( "/U" )) ||
|
||
!(CopyAttrArgument->Initialize( "/K" )) ||
|
||
!(UseShortArgument->Initialize( "/N" )) ||
|
||
!(RestartableArgument->Initialize( "/Z" )) ||
|
||
!(ExclusionListArgument->Initialize("/EXCLUDE:*")) ||
|
||
!(InvalidSwitchArgument->Initialize( "/*" )) ||
|
||
//
|
||
// Put the arguments in the argument array
|
||
//
|
||
!(ArgArray.Put( &ProgramNameArgument )) ||
|
||
!(ArgArray.Put( ArchiveArgument )) ||
|
||
!(ArgArray.Put( DateArgument )) ||
|
||
!(ArgArray.Put( OldArgument )) ||
|
||
!(ArgArray.Put( EmptyArgument )) ||
|
||
!(ArgArray.Put( ModifyArgument )) ||
|
||
!(ArgArray.Put( PromptArgument )) ||
|
||
!(ArgArray.Put( SubdirArgument )) ||
|
||
!(ArgArray.Put( VerifyArgument )) ||
|
||
!(ArgArray.Put( WaitArgument )) ||
|
||
!(ArgArray.Put( HelpArgument )) ||
|
||
!(ArgArray.Put( ContinueArgument )) ||
|
||
!(ArgArray.Put( IntelligentArgument )) ||
|
||
!(ArgArray.Put( VerboseArgument )) ||
|
||
!(ArgArray.Put( HiddenArgument )) ||
|
||
!(ArgArray.Put( ReadOnlyArgument )) ||
|
||
!(ArgArray.Put( SilentArgument )) ||
|
||
!(ArgArray.Put( RestartableArgument )) ||
|
||
!(ArgArray.Put( NoCopyArgument )) ||
|
||
!(ArgArray.Put( StructureArgument )) ||
|
||
!(ArgArray.Put( UpdateArgument )) ||
|
||
!(ArgArray.Put( CopyAttrArgument )) ||
|
||
!(ArgArray.Put( UseShortArgument )) ||
|
||
!(ArgArray.Put( ExclusionListArgument )) ||
|
||
!(ArgArray.Put( InvalidSwitchArgument )) ||
|
||
!(ArgArray.Put( FirstQuotedPathArgument )) ||
|
||
!(ArgArray.Put( SecondQuotedPathArgument )) ||
|
||
!(ArgArray.Put( FirstPathArgument )) ||
|
||
!(ArgArray.Put( SecondPathArgument ))
|
||
) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR);
|
||
}
|
||
|
||
//
|
||
// Parse the arguments
|
||
//
|
||
ArgLex = ParseArguments( &CmdLine, &ArgArray );
|
||
|
||
if ( InvalidSwitchArgument->IsValueSet() ) {
|
||
|
||
InvalidSwitch = InvalidSwitchArgument->GetString();
|
||
|
||
InvalidParms.Initialize( SWITCH_CHARACTERS );
|
||
|
||
Ch = InvalidSwitch->QueryChAt(0);
|
||
|
||
if ( Ch == 'd' || Ch == 'D' ) {
|
||
Ch = InvalidSwitch->QueryChAt(1);
|
||
if ( Ch == INVALID_CHAR ) {
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
} else if ( Ch != ':' || InvalidSwitch->QueryChCount() == 2 ) {
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_SWITCH_SWITCH,
|
||
InvalidSwitchArgument->GetLexeme(),
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
} else if ( Ch == '/' ) {
|
||
Ch = InvalidSwitch->QueryChAt(1);
|
||
if ( Ch == ':' && InvalidSwitchArgument->GetString()->QueryChAt(2) == INVALID_CHAR ) {
|
||
InvalidSwitchArgument->GetLexeme()->Truncate(1);
|
||
}
|
||
}
|
||
|
||
Ch = InvalidSwitch->QueryChAt(0);
|
||
|
||
if ( InvalidParms.Strchr( Ch ) != INVALID_CHNUM ) {
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_PARAMETER,
|
||
InvalidSwitchArgument->GetLexeme(),
|
||
EXIT_MISC_ERROR );
|
||
} else {
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_SWITCH_SWITCH,
|
||
InvalidSwitchArgument->GetLexeme(),
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the switches
|
||
//
|
||
_EmptySwitch = EmptyArgument->QueryFlag();
|
||
_ModifySwitch = ModifyArgument->QueryFlag();
|
||
|
||
//
|
||
// ModifySwitch implies ArchiveSwitch
|
||
//
|
||
if ( _ModifySwitch ) {
|
||
_ArchiveSwitch = TRUE;
|
||
} else {
|
||
_ArchiveSwitch = ArchiveArgument->QueryFlag();
|
||
}
|
||
|
||
//
|
||
// Set the switches
|
||
//
|
||
_PromptSwitch = PromptArgument->QueryFlag();
|
||
_SubdirSwitch = SubdirArgument->QueryFlag();
|
||
_VerifySwitch = VerifyArgument->QueryFlag();
|
||
_WaitSwitch = WaitArgument->QueryFlag();
|
||
_ContinueSwitch = ContinueArgument->QueryFlag();
|
||
_IntelligentSwitch = IntelligentArgument->QueryFlag();
|
||
_CopyIfOldSwitch = OldArgument->QueryFlag();
|
||
_VerboseSwitch = VerboseArgument->QueryFlag();
|
||
_HiddenSwitch = HiddenArgument->QueryFlag();
|
||
_ReadOnlySwitch = ReadOnlyArgument->QueryFlag();
|
||
_SilentSwitch = SilentArgument->QueryFlag();
|
||
_DontCopySwitch = NoCopyArgument->QueryFlag();
|
||
_StructureOnlySwitch= StructureArgument->QueryFlag();
|
||
_UpdateSwitch = UpdateArgument->QueryFlag();
|
||
_CopyAttrSwitch = CopyAttrArgument->QueryFlag();
|
||
_UseShortSwitch = UseShortArgument->QueryFlag();
|
||
_RestartableSwitch = RestartableArgument->QueryFlag();
|
||
HelpSwitch = HelpArgument->QueryFlag();
|
||
|
||
|
||
//
|
||
// Set the source and destination paths. Argument checking is
|
||
// done somewhere else, so it is ok. to set the source path to
|
||
// NULL here.
|
||
//
|
||
GetSourceAndDestinationPath(FirstPathArgument,
|
||
FirstQuotedPathArgument,
|
||
SecondPathArgument,
|
||
SecondQuotedPathArgument,
|
||
ArgLex,
|
||
&_SourcePath,
|
||
&_DestinationPath);
|
||
|
||
DELETE(ArgLex);
|
||
|
||
//
|
||
// Set the date argument
|
||
//
|
||
|
||
if ( DateArgument->IsValueSet() ) {
|
||
|
||
if ((_Date = NEW TIMEINFO) == NULL ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
|
||
}
|
||
|
||
_Date->Initialize( DateArgument->GetTimeInfo() );
|
||
|
||
} else {
|
||
|
||
_Date = NULL;
|
||
}
|
||
|
||
if( ExclusionListArgument->IsValueSet() ) {
|
||
|
||
InitializeExclusionList( ExclusionListArgument->GetString() );
|
||
}
|
||
}
|
||
|
||
PARGUMENT_LEXEMIZER
|
||
XCOPY::ParseArguments(
|
||
IN PWSTRING CmdLine,
|
||
OUT PARRAY ArgArray
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Parses a group of arguments
|
||
|
||
Arguments:
|
||
|
||
CmdLine - Supplies pointer to a command line to parse
|
||
ArgArray - Supplies pointer to array of arguments
|
||
|
||
Return Value:
|
||
|
||
Returns the argument lexemizer used which then needs to be freed
|
||
by the client.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
{
|
||
PARGUMENT_LEXEMIZER ArgLex;
|
||
|
||
//
|
||
// Initialize lexeme array and the lexemizer.
|
||
//
|
||
if ( !(ArgLex = NEW ARGUMENT_LEXEMIZER) ||
|
||
!(LexArray->Initialize( 9, 9 )) ||
|
||
!(ArgLex->Initialize( LexArray )) ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
|
||
//
|
||
// Set our parsing preferences
|
||
//
|
||
ArgLex->PutMultipleSwitch( "/?AMDPSEVWCIFHRQLKTUNZ" );
|
||
ArgLex->PutSwitches( "/" );
|
||
ArgLex->SetCaseSensitive( FALSE );
|
||
ArgLex->PutSeparators( " \t" );
|
||
ArgLex->PutStartQuotes( "\"" );
|
||
ArgLex->PutEndQuotes( "\"" );
|
||
ArgLex->SetAllowSwitchGlomming( TRUE );
|
||
ArgLex->SetNoSpcBetweenDstAndSwitch( TRUE );
|
||
|
||
//
|
||
// Parse the arguments
|
||
//
|
||
if ( !(ArgLex->PrepareToParse( CmdLine ))) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_PARSE,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
|
||
}
|
||
|
||
if ( !ArgLex->DoParsing( ArgArray ) ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
|
||
return ArgLex;
|
||
}
|
||
|
||
VOID
|
||
XCOPY::CheckArgumentConsistency (
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks the consistency of the arguments
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
{
|
||
PFSN_DIRECTORY DirSrc;
|
||
PFSN_DIRECTORY DirDst;
|
||
PWSTRING DevSrc;
|
||
PWSTRING DevDst;
|
||
PATH PathSrc, PathSrc1;
|
||
PATH PathDst, PathDst1;
|
||
DSTRING Slash;
|
||
|
||
if ( HelpSwitch ) {
|
||
//
|
||
// Help requested
|
||
//
|
||
Usage();
|
||
DisplayMessageAndExit( 0,
|
||
NULL,
|
||
EXIT_NORMAL );
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure that we have a source path
|
||
//
|
||
if ( _SourcePath == NULL ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
|
||
//
|
||
// The empty switch implies Subdir switch (note that DOS
|
||
// requires Subdir switch explicitly, but we are not that
|
||
// brain-damaged).
|
||
//
|
||
if ( _EmptySwitch ) {
|
||
_SubdirSwitch = TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// The StructureOnly switch imples the subdir switch
|
||
//
|
||
if ( _StructureOnlySwitch ) {
|
||
_SubdirSwitch = TRUE;
|
||
}
|
||
|
||
//
|
||
// If destination path is null, then the destination path is the
|
||
// current directory
|
||
//
|
||
if ( _DestinationPath == NULL ) {
|
||
|
||
if ( ((_DestinationPath = NEW PATH) == NULL ) ||
|
||
!_DestinationPath->Initialize( (LPWSTR)L".", TRUE ) ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
|
||
}
|
||
}
|
||
|
||
_DestinationPath->TruncateNameAtColon();
|
||
|
||
if ( !PathSrc1.Initialize( _SourcePath, TRUE ) ||
|
||
!PathDst1.Initialize( _DestinationPath, TRUE ) ||
|
||
!(DevSrc = PathSrc1.QueryDevice()) ||
|
||
!(DevDst = PathDst1.QueryDevice()) ||
|
||
!PathSrc.Initialize( DevSrc ) ||
|
||
!PathDst.Initialize( DevDst ) ||
|
||
!Slash.Initialize( "\\" ) ||
|
||
!PathSrc.AppendBase( &Slash ) ||
|
||
!PathDst.AppendBase( &Slash ) ||
|
||
!(DirSrc = SYSTEM::QueryDirectory( &PathSrc )) ||
|
||
!(DirDst = SYSTEM::QueryDirectory( &PathDst )) ) {
|
||
DisplayMessageAndExit( XCOPY_ERROR_INVALID_DRIVE, NULL, EXIT_MISC_ERROR );
|
||
}
|
||
DELETE( DevSrc );
|
||
DELETE( DevDst );
|
||
DELETE( DirSrc );
|
||
DELETE( DirDst );
|
||
}
|
||
|
||
BOOLEAN
|
||
XCOPY::AddToExclusionList(
|
||
IN PWSTRING ExclusionListFileName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method adds the contents of the specified file to
|
||
the exclusion list.
|
||
|
||
Arguments:
|
||
|
||
ExclusionListFileName -- Supplies the name of a file which
|
||
contains the exclusion list.
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
PATH ExclusionPath;
|
||
PDSTRING String;
|
||
PFSN_FILE File;
|
||
PFILE_STREAM Stream;
|
||
CHNUM Position;
|
||
|
||
DebugPtrAssert( ExclusionListFileName );
|
||
|
||
if( !ExclusionPath.Initialize( ExclusionListFileName ) ||
|
||
(File = SYSTEM::QueryFile( &ExclusionPath )) == NULL ||
|
||
(Stream = File->QueryStream( READ_ACCESS )) == NULL ) {
|
||
|
||
DisplayMessageAndExit( MSG_COMP_UNABLE_TO_READ,
|
||
ExclusionListFileName,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
|
||
while( !Stream->IsAtEnd() &&
|
||
(String = NEW DSTRING) != NULL &&
|
||
Stream->ReadLine ( String ) ) {
|
||
|
||
if( String->QueryChCount() == 0 ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
// Convert the string to upper-case and remove
|
||
// trailing whitespace (blanks and tabs).
|
||
//
|
||
String->Strupr();
|
||
Position = String->QueryChCount() - 1;
|
||
|
||
while( Position != 0 &&
|
||
(String->QueryChAt( Position ) == ' ' ||
|
||
String->QueryChAt( Position ) == '\t') ) {
|
||
|
||
Position -= 1;
|
||
}
|
||
|
||
if( String->QueryChAt( Position ) != ' ' &&
|
||
String->QueryChAt( Position ) != '\t' ) {
|
||
|
||
Position++;
|
||
}
|
||
|
||
if( Position != String->QueryChCount() ) {
|
||
|
||
String->Truncate( Position );
|
||
}
|
||
|
||
if( String->QueryChCount() != 0 && !_ExclusionList->Put( String ) ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
}
|
||
|
||
DELETE( Stream );
|
||
DELETE( File );
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
XCOPY::InitializeExclusionList(
|
||
IN PWSTRING ListOfFiles
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This method reads the exclusion list and initializes the
|
||
exclusion list array.
|
||
|
||
Arguments:
|
||
|
||
ListOfFiles -- Supplies a string containing a list of file
|
||
names, separated by '+' (e.g. file1+file2+file3)
|
||
|
||
Return Value:
|
||
|
||
TRUE upon successful completion.
|
||
|
||
--*/
|
||
{
|
||
DSTRING CurrentName;
|
||
CHNUM LastPosition, Position;
|
||
|
||
DebugPtrAssert( ListOfFiles );
|
||
|
||
if( (_ExclusionList = NEW STRING_ARRAY) == NULL ||
|
||
!_ExclusionList->Initialize() ||
|
||
(_Iterator = _ExclusionList->QueryIterator()) == NULL ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
|
||
}
|
||
|
||
LastPosition = 0;
|
||
|
||
while( LastPosition != ListOfFiles->QueryChCount() ) {
|
||
|
||
Position = ListOfFiles->Strchr( '+', LastPosition );
|
||
|
||
if( Position == INVALID_CHNUM ) {
|
||
|
||
Position = ListOfFiles->QueryChCount();
|
||
}
|
||
|
||
if( Position != LastPosition ) {
|
||
|
||
if( !CurrentName.Initialize( ListOfFiles,
|
||
LastPosition,
|
||
Position - LastPosition ) ) {
|
||
|
||
DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
|
||
NULL,
|
||
EXIT_MISC_ERROR );
|
||
}
|
||
|
||
AddToExclusionList( &CurrentName );
|
||
}
|
||
|
||
// Advance past any separators.
|
||
//
|
||
while( Position < ListOfFiles->QueryChCount() &&
|
||
ListOfFiles->QueryChAt( Position ) == '+' ) {
|
||
|
||
Position += 1;
|
||
}
|
||
|
||
LastPosition = Position;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|