1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/FileMasks.cpp
Martin Prikryl 43ec878d4b 5.2.2
2018-01-11 15:30:52 +01:00

1202 lines
35 KiB
C++

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "FileMasks.h"
#include "Common.h"
#include "TextsCore.h"
#include "RemoteFiles.h"
#include "PuttyTools.h"
#include "Terminal.h"
#include <StrUtils.hpp>
//---------------------------------------------------------------------------
extern const wchar_t IncludeExcludeFileMasksDelimiter = L'|';
static UnicodeString FileMasksDelimiters = L";,";
static UnicodeString AllFileMasksDelimiters = FileMasksDelimiters + IncludeExcludeFileMasksDelimiter;
static UnicodeString DirectoryMaskDelimiters = L"/\\";
static UnicodeString FileMasksDelimiterStr = UnicodeString(FileMasksDelimiters[1]) + L' ';
//---------------------------------------------------------------------------
__fastcall EFileMasksException::EFileMasksException(
UnicodeString Message, int AErrorStart, int AErrorLen) :
Exception(Message)
{
ErrorStart = AErrorStart;
ErrorLen = AErrorLen;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall MaskFilePart(const UnicodeString Part, const UnicodeString Mask, bool& Masked)
{
UnicodeString Result;
int RestStart = 1;
bool Delim = false;
for (int Index = 1; Index <= Mask.Length(); Index++)
{
switch (Mask[Index])
{
case L'\\':
if (!Delim)
{
Delim = true;
Masked = true;
break;
}
case L'*':
if (!Delim)
{
Result += Part.SubString(RestStart, Part.Length() - RestStart + 1);
RestStart = Part.Length() + 1;
Masked = true;
break;
}
case L'?':
if (!Delim)
{
if (RestStart <= Part.Length())
{
Result += Part[RestStart];
RestStart++;
}
Masked = true;
break;
}
default:
Result += Mask[Index];
RestStart++;
Delim = false;
break;
}
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall MaskFileName(UnicodeString FileName, const UnicodeString Mask)
{
if (IsEffectiveFileNameMask(Mask))
{
bool Masked;
int P = Mask.LastDelimiter(L".");
if (P > 0)
{
int P2 = FileName.LastDelimiter(".");
// only dot at beginning of file name is not considered as
// name/ext separator
UnicodeString FileExt = P2 > 1 ?
FileName.SubString(P2 + 1, FileName.Length() - P2) : UnicodeString();
FileExt = MaskFilePart(FileExt, Mask.SubString(P + 1, Mask.Length() - P), Masked);
if (P2 > 1)
{
FileName.SetLength(P2 - 1);
}
FileName = MaskFilePart(FileName, Mask.SubString(1, P - 1), Masked);
if (!FileExt.IsEmpty())
{
FileName += L"." + FileExt;
}
}
else
{
FileName = MaskFilePart(FileName, Mask, Masked);
}
}
return FileName;
}
//---------------------------------------------------------------------------
bool __fastcall IsEffectiveFileNameMask(const UnicodeString & Mask)
{
return !Mask.IsEmpty() && (Mask != L"*") && (Mask != L"*.*");
}
//---------------------------------------------------------------------------
UnicodeString __fastcall DelimitFileNameMask(UnicodeString Mask)
{
for (int i = 1; i <= Mask.Length(); i++)
{
if (wcschr(L"\\*?", Mask[i]) != NULL)
{
Mask.Insert(L"\\", i);
i++;
}
}
return Mask;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TFileMasks::TParams::TParams() :
Size(0)
{
}
//---------------------------------------------------------------------------
UnicodeString TFileMasks::TParams::ToString() const
{
return UnicodeString(L"[") + IntToStr(Size) + L"/" + DateTimeToStr(Modification) + L"]";
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::IsMask(const UnicodeString Mask)
{
return (Mask.LastDelimiter(L"?*[") > 0);
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::IsAnyMask(const UnicodeString & Mask)
{
return Mask.IsEmpty() || (Mask == L"*.*") || (Mask == L"*");
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TFileMasks::NormalizeMask(const UnicodeString & Mask, const UnicodeString & AnyMask)
{
if (IsAnyMask(Mask))
{
return AnyMask;
}
else
{
return Mask;
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TFileMasks::ComposeMaskStr(
TStrings * MasksStr, bool Directory)
{
UnicodeString Result;
UnicodeString ResultNoDirMask;
for (int I = 0; I < MasksStr->Count; I++)
{
UnicodeString Str = MasksStr->Strings[I].Trim();
if (!Str.IsEmpty())
{
for (int P = 1; P <= Str.Length(); P++)
{
if (Str.IsDelimiter(AllFileMasksDelimiters, P))
{
Str.Insert(Str[P], P);
P++;
}
}
UnicodeString StrNoDirMask;
if (Directory)
{
StrNoDirMask = Str;
Str = MakeDirectoryMask(Str);
}
else
{
while (Str.IsDelimiter(DirectoryMaskDelimiters, Str.Length()))
{
Str.SetLength(Str.Length() - 1);
}
StrNoDirMask = Str;
}
AddToList(Result, Str, FileMasksDelimiterStr);
AddToList(ResultNoDirMask, StrNoDirMask, FileMasksDelimiterStr);
}
}
// For directories, the above will add slash ay the end of masks,
// breaking size and time masks and thus circumverting their validation.
// This performes as hoc validation to cover the scenario.
// For files this makes no difference, but no harm either
TFileMasks Temp(Directory ? 1 : 0);
Temp = ResultNoDirMask;
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TFileMasks::ComposeMaskStr(
TStrings * IncludeFileMasksStr, TStrings * ExcludeFileMasksStr,
TStrings * IncludeDirectoryMasksStr, TStrings * ExcludeDirectoryMasksStr)
{
UnicodeString IncludeMasks = ComposeMaskStr(IncludeFileMasksStr, false);
AddToList(IncludeMasks, ComposeMaskStr(IncludeDirectoryMasksStr, true), FileMasksDelimiterStr);
UnicodeString ExcludeMasks = ComposeMaskStr(ExcludeFileMasksStr, false);
AddToList(ExcludeMasks, ComposeMaskStr(ExcludeDirectoryMasksStr, true), FileMasksDelimiterStr);
UnicodeString Result = IncludeMasks;
if (!ExcludeMasks.IsEmpty())
{
if (!Result.IsEmpty())
{
Result += L' ';
}
Result += UnicodeString(IncludeExcludeFileMasksDelimiter) + L' ' + ExcludeMasks;
}
return Result;
}
//---------------------------------------------------------------------------
__fastcall TFileMasks::TFileMasks()
{
Init();
}
//---------------------------------------------------------------------------
__fastcall TFileMasks::TFileMasks(int ForceDirectoryMasks)
{
Init();
FForceDirectoryMasks = ForceDirectoryMasks;
}
//---------------------------------------------------------------------------
__fastcall TFileMasks::TFileMasks(const TFileMasks & Source)
{
Init();
FForceDirectoryMasks = Source.FForceDirectoryMasks;
SetStr(Source.Masks, false);
}
//---------------------------------------------------------------------------
__fastcall TFileMasks::TFileMasks(const UnicodeString & AMasks)
{
Init();
SetStr(AMasks, false);
}
//---------------------------------------------------------------------------
__fastcall TFileMasks::~TFileMasks()
{
Clear();
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::Init()
{
FForceDirectoryMasks = -1;
DoInit(false);
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::DoInit(bool Delete)
{
for (int Index = 0; Index < 4; Index++)
{
if (Delete)
{
delete FMasksStr[Index];
}
FMasksStr[Index] = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::Clear()
{
DoInit(true);
for (int Index = 0; Index < 4; Index++)
{
Clear(FMasks[Index]);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::Clear(TMasks & Masks)
{
TMasks::iterator I = Masks.begin();
while (I != Masks.end())
{
ReleaseMaskMask((*I).FileNameMask);
ReleaseMaskMask((*I).DirectoryMask);
I++;
}
Masks.clear();
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::MatchesMasks(const UnicodeString FileName, bool Directory,
const UnicodeString Path, const TParams * Params, const TMasks & Masks, bool Recurse)
{
bool Result = false;
TMasks::const_iterator I = Masks.begin();
while (!Result && (I != Masks.end()))
{
const TMask & Mask = *I;
Result =
MatchesMaskMask(Mask.DirectoryMask, Path) &&
MatchesMaskMask(Mask.FileNameMask, FileName);
if (Result)
{
bool HasSize = (Params != NULL);
switch (Mask.HighSizeMask)
{
case TMask::None:
Result = true;
break;
case TMask::Open:
Result = HasSize && (Params->Size < Mask.HighSize);
break;
case TMask::Close:
Result = HasSize && (Params->Size <= Mask.HighSize);
break;
}
if (Result)
{
switch (Mask.LowSizeMask)
{
case TMask::None:
Result = true;
break;
case TMask::Open:
Result = HasSize && (Params->Size > Mask.LowSize);
break;
case TMask::Close:
Result = HasSize && (Params->Size >= Mask.LowSize);
break;
}
}
bool HasModification = (Params != NULL);
if (Result)
{
switch (Mask.HighModificationMask)
{
case TMask::None:
Result = true;
break;
case TMask::Open:
Result = HasModification && (Params->Modification < Mask.HighModification);
break;
case TMask::Close:
Result = HasModification && (Params->Modification <= Mask.HighModification);
break;
}
}
if (Result)
{
switch (Mask.LowModificationMask)
{
case TMask::None:
Result = true;
break;
case TMask::Open:
Result = HasModification && (Params->Modification > Mask.LowModification);
break;
case TMask::Close:
Result = HasModification && (Params->Modification >= Mask.LowModification);
break;
}
}
}
I++;
}
if (!Result && Directory && !IsUnixRootPath(Path) && Recurse)
{
UnicodeString ParentFileName = UnixExtractFileName(Path);
UnicodeString ParentPath = UnixExcludeTrailingBackslash(UnixExtractFilePath(Path));
// Pass Params down or not?
// Currently it includes Size/Time only, what is not used for directories.
// So it depends of future use. Possibly we should make a copy
// and pass on only relevant fields.
Result = MatchesMasks(ParentFileName, true, ParentPath, Params, Masks, Recurse);
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::Matches(const UnicodeString FileName, bool Directory,
const UnicodeString Path, const TParams * Params) const
{
bool ImplicitMatch;
return Matches(FileName, Directory, Path, Params, ImplicitMatch);
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::Matches(const UnicodeString FileName, bool Directory,
const UnicodeString Path, const TParams * Params,
bool & ImplicitMatch) const
{
bool ImplicitIncludeMatch = FMasks[MASK_INDEX(Directory, true)].empty();
bool ExplicitIncludeMatch = MatchesMasks(FileName, Directory, Path, Params, FMasks[MASK_INDEX(Directory, true)], true);
bool Result =
(ImplicitIncludeMatch || ExplicitIncludeMatch) &&
!MatchesMasks(FileName, Directory, Path, Params, FMasks[MASK_INDEX(Directory, false)], false);
ImplicitMatch =
Result && ImplicitIncludeMatch && !ExplicitIncludeMatch &&
FMasks[MASK_INDEX(Directory, false)].empty();
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::Matches(const UnicodeString FileName, bool Local,
bool Directory, const TParams * Params) const
{
bool ImplicitMatch;
return Matches(FileName, Local, Directory, Params, ImplicitMatch);
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::Matches(const UnicodeString FileName, bool Local,
bool Directory, const TParams * Params, bool & ImplicitMatch) const
{
bool Result;
if (Local)
{
UnicodeString Path = ExtractFilePath(FileName);
if (!Path.IsEmpty())
{
Path = ToUnixPath(ExcludeTrailingBackslash(Path));
}
Result = Matches(ExtractFileName(FileName), Directory, Path, Params,
ImplicitMatch);
}
else
{
Result = Matches(UnixExtractFileName(FileName), Directory,
UnixExcludeTrailingBackslash(UnixExtractFilePath(FileName)), Params,
ImplicitMatch);
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::operator ==(const TFileMasks & rhm) const
{
return (Masks == rhm.Masks);
}
//---------------------------------------------------------------------------
TFileMasks & __fastcall TFileMasks::operator =(const UnicodeString & rhs)
{
Masks = rhs;
return *this;
}
//---------------------------------------------------------------------------
TFileMasks & __fastcall TFileMasks::operator =(const TFileMasks & rhm)
{
FForceDirectoryMasks = rhm.FForceDirectoryMasks;
Masks = rhm.Masks;
return *this;
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::operator ==(const UnicodeString & rhs) const
{
return (Masks == rhs);
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::ThrowError(int Start, int End)
{
throw EFileMasksException(
FMTLOAD(MASK_ERROR, (Masks.SubString(Start, End - Start + 1))),
Start, End - Start + 1);
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::CreateMaskMask(const UnicodeString & Mask, int Start, int End,
bool Ex, TMaskMask & MaskMask)
{
try
{
assert(MaskMask.Mask == NULL);
if (Ex && IsAnyMask(Mask))
{
MaskMask.Kind = TMaskMask::Any;
MaskMask.Mask = NULL;
}
else
{
MaskMask.Kind = (Ex && (Mask == L"*.")) ? TMaskMask::NoExt : TMaskMask::Regular;
MaskMask.Mask = new Masks::TMask(Mask);
}
}
catch(...)
{
ThrowError(Start, End);
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TFileMasks::MakeDirectoryMask(UnicodeString Str)
{
assert(!Str.IsEmpty());
if (Str.IsEmpty() || !Str.IsDelimiter(DirectoryMaskDelimiters, Str.Length()))
{
int D = Str.LastDelimiter(DirectoryMaskDelimiters);
// if there's any [back]slash anywhere in str,
// add the same [back]slash at the end, otherwise add slash
wchar_t Delimiter = (D > 0) ? Str[D] : DirectoryMaskDelimiters[1];
Str += Delimiter;
}
return Str;
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::CreateMask(
const UnicodeString & MaskStr, int MaskStart, int /*MaskEnd*/, bool Include)
{
bool Directory = false; // shut up
TMask Mask;
Mask.MaskStr = MaskStr;
Mask.UserStr = MaskStr;
Mask.FileNameMask.Kind = TMaskMask::Any;
Mask.FileNameMask.Mask = NULL;
Mask.DirectoryMask.Kind = TMaskMask::Any;
Mask.DirectoryMask.Mask = NULL;
Mask.HighSizeMask = TMask::None;
Mask.LowSizeMask = TMask::None;
Mask.HighModificationMask = TMask::None;
Mask.LowModificationMask = TMask::None;
wchar_t NextPartDelimiter = L'\0';
int NextPartFrom = 1;
while (NextPartFrom <= MaskStr.Length())
{
wchar_t PartDelimiter = NextPartDelimiter;
int PartFrom = NextPartFrom;
UnicodeString PartStr = CopyToChars(MaskStr, NextPartFrom, L"<>", false, &NextPartDelimiter, true);
int PartStart = MaskStart + PartFrom - 1;
int PartEnd = MaskStart + NextPartFrom - 1 - 2;
TrimEx(PartStr, PartStart, PartEnd);
if (PartDelimiter != L'\0')
{
bool Low = (PartDelimiter == L'>');
TMask::TMaskBoundary Boundary;
if ((PartStr.Length() >= 1) && (PartStr[1] == L'='))
{
Boundary = TMask::Close;
PartStr.Delete(1, 1);
}
else
{
Boundary = TMask::Open;
}
TFormatSettings FormatSettings = TFormatSettings::Create(GetDefaultLCID());
FormatSettings.DateSeparator = L'-';
FormatSettings.TimeSeparator = L':';
FormatSettings.ShortDateFormat = "yyyy/mm/dd";
FormatSettings.ShortTimeFormat = "hh:nn:ss";
TDateTime Modification;
if (TryStrToDateTime(PartStr, Modification, FormatSettings) ||
TryRelativeStrToDateTime(PartStr, Modification))
{
TMask::TMaskBoundary & ModificationMask =
(Low ? Mask.LowModificationMask : Mask.HighModificationMask);
if ((ModificationMask != TMask::None) || Directory)
{
// include delimiter into size part
ThrowError(PartStart - 1, PartEnd);
}
ModificationMask = Boundary;
(Low ? Mask.LowModification : Mask.HighModification) = Modification;
}
else
{
TMask::TMaskBoundary & SizeMask = (Low ? Mask.LowSizeMask : Mask.HighSizeMask);
__int64 & Size = (Low ? Mask.LowSize : Mask.HighSize);
if ((SizeMask != TMask::None) || Directory)
{
// include delimiter into size part
ThrowError(PartStart - 1, PartEnd);
}
SizeMask = Boundary;
Size = ParseSize(PartStr);
}
}
else if (!PartStr.IsEmpty())
{
int D = PartStr.LastDelimiter(DirectoryMaskDelimiters);
Directory = (D > 0) && (D == PartStr.Length());
if (Directory)
{
do
{
PartStr.SetLength(PartStr.Length() - 1);
Mask.UserStr.Delete(PartStart - MaskStart + D, 1);
D--;
}
while (PartStr.IsDelimiter(DirectoryMaskDelimiters, PartStr.Length()));
D = PartStr.LastDelimiter(DirectoryMaskDelimiters);
if (FForceDirectoryMasks == 0)
{
Directory = false;
Mask.MaskStr = Mask.UserStr;
}
}
else if (FForceDirectoryMasks > 0)
{
Directory = true;
Mask.MaskStr.Insert(DirectoryMaskDelimiters[1], PartStart - MaskStart + PartStr.Length());
}
if (D > 0)
{
// make sure sole "/" (root dir) is preserved as is
CreateMaskMask(
UnixExcludeTrailingBackslash(ToUnixPath(PartStr.SubString(1, D))),
PartStart, PartStart + D - 1, false,
Mask.DirectoryMask);
CreateMaskMask(
PartStr.SubString(D + 1, PartStr.Length() - D),
PartStart + D, PartEnd, true,
Mask.FileNameMask);
}
else
{
CreateMaskMask(PartStr, PartStart, PartEnd, true, Mask.FileNameMask);
}
}
}
FMasks[MASK_INDEX(Directory, Include)].push_back(Mask);
}
//---------------------------------------------------------------------------
TStrings * __fastcall TFileMasks::GetMasksStr(int Index) const
{
if (FMasksStr[Index] == NULL)
{
FMasksStr[Index] = new TStringList();
TMasks::const_iterator I = FMasks[Index].begin();
while (I != FMasks[Index].end())
{
FMasksStr[Index]->Add((*I).UserStr);
I++;
}
}
return FMasksStr[Index];
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::ReleaseMaskMask(TMaskMask & MaskMask)
{
delete MaskMask.Mask;
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::TrimEx(UnicodeString & Str, int & Start, int & End)
{
UnicodeString Buf = TrimLeft(Str);
Start += Str.Length() - Buf.Length();
Str = TrimRight(Buf);
End -= Buf.Length() - Str.Length();
}
//---------------------------------------------------------------------------
bool __fastcall TFileMasks::MatchesMaskMask(const TMaskMask & MaskMask, const UnicodeString & Str)
{
bool Result;
if (MaskMask.Kind == TMaskMask::Any)
{
Result = true;
}
else if ((MaskMask.Kind == TMaskMask::NoExt) && (Str.Pos(L".") == 0))
{
Result = true;
}
else
{
Result = MaskMask.Mask->Matches(Str);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::SetMasks(const UnicodeString value)
{
if (FStr != value)
{
SetStr(value, false);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::SetMask(const UnicodeString & Mask)
{
SetStr(Mask, true);
}
//---------------------------------------------------------------------------
void __fastcall TFileMasks::SetStr(const UnicodeString Str, bool SingleMask)
{
UnicodeString Backup = FStr;
try
{
FStr = Str;
Clear();
int NextMaskFrom = 1;
bool Include = true;
while (NextMaskFrom <= Str.Length())
{
int MaskStart = NextMaskFrom;
wchar_t NextMaskDelimiter;
UnicodeString MaskStr;
if (SingleMask)
{
MaskStr = Str;
NextMaskFrom = Str.Length() + 1;
NextMaskDelimiter = L'\0';
}
else
{
MaskStr = CopyToChars(Str, NextMaskFrom, AllFileMasksDelimiters, false, &NextMaskDelimiter, true);
}
int MaskEnd = NextMaskFrom - 2;
TrimEx(MaskStr, MaskStart, MaskEnd);
if (!MaskStr.IsEmpty())
{
CreateMask(MaskStr, MaskStart, MaskEnd, Include);
}
if (NextMaskDelimiter == IncludeExcludeFileMasksDelimiter)
{
if (Include)
{
Include = false;
}
else
{
ThrowError(NextMaskFrom - 1, Str.Length());
}
}
}
}
catch(...)
{
// this does not work correctly if previous mask was set using SetMask.
// this should not fail (the mask was validated before),
// otherwise we end in an infinite loop
SetStr(Backup, false);
throw;
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#define TEXT_TOKEN L'\255'
//---------------------------------------------------------------------------
const wchar_t TCustomCommand::NoQuote = L'\0';
const UnicodeString TCustomCommand::Quotes = L"\"'";
//---------------------------------------------------------------------------
TCustomCommand::TCustomCommand()
{
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommand::GetToken(
const UnicodeString & Command, int Index, int & Len, wchar_t & PatternCmd)
{
assert(Index <= Command.Length());
const wchar_t * Ptr = Command.c_str() + Index - 1;
if (Ptr[0] == L'!')
{
PatternCmd = Ptr[1];
if (PatternCmd == L'\0')
{
Len = 1;
}
else if (PatternCmd == L'!')
{
Len = 2;
}
else
{
Len = PatternLen(Command, Index);
}
if (Len <= 0)
{
throw Exception(FMTLOAD(CUSTOM_COMMAND_UNKNOWN, (PatternCmd, Index)));
}
else
{
if ((Command.Length() - Index + 1) < Len)
{
throw Exception(FMTLOAD(CUSTOM_COMMAND_UNTERMINATED, (PatternCmd, Index)));
}
}
}
else
{
PatternCmd = TEXT_TOKEN;
const wchar_t * NextPattern = wcschr(Ptr, L'!');
if (NextPattern == NULL)
{
Len = Command.Length() - Index + 1;
}
else
{
Len = NextPattern - Ptr;
}
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomCommand::Complete(const UnicodeString & Command,
bool LastPass)
{
UnicodeString Result;
int Index = 1;
while (Index <= Command.Length())
{
int Len;
wchar_t PatternCmd;
GetToken(Command, Index, Len, PatternCmd);
if (PatternCmd == TEXT_TOKEN)
{
Result += Command.SubString(Index, Len);
}
else if (PatternCmd == L'!')
{
if (LastPass)
{
Result += L'!';
}
else
{
Result += Command.SubString(Index, Len);
}
}
else
{
wchar_t Quote = NoQuote;
if ((Index > 1) && (Index + Len - 1 < Command.Length()) &&
Command.IsDelimiter(Quotes, Index - 1) &&
Command.IsDelimiter(Quotes, Index + Len) &&
(Command[Index - 1] == Command[Index + Len]))
{
Quote = Command[Index - 1];
}
UnicodeString Pattern = Command.SubString(Index, Len);
UnicodeString Replacement;
bool Delimit = true;
if (PatternReplacement(Pattern, Replacement, Delimit))
{
if (!LastPass)
{
Replacement = ReplaceStr(Replacement, L"!", L"!!");
}
if (Delimit)
{
DelimitReplacement(Replacement, Quote);
}
Result += Replacement;
}
else
{
Result += Pattern;
}
}
Index += Len;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommand::DelimitReplacement(UnicodeString & Replacement, wchar_t Quote)
{
Replacement = ShellDelimitStr(Replacement, Quote);
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommand::Validate(const UnicodeString & Command)
{
CustomValidate(Command, NULL);
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommand::CustomValidate(const UnicodeString & Command,
void * Arg)
{
int Index = 1;
while (Index <= Command.Length())
{
int Len;
wchar_t PatternCmd;
GetToken(Command, Index, Len, PatternCmd);
ValidatePattern(Command, Index, Len, PatternCmd, Arg);
Index += Len;
}
}
//---------------------------------------------------------------------------
bool __fastcall TCustomCommand::FindPattern(const UnicodeString & Command,
wchar_t PatternCmd)
{
bool Result = false;
int Index = 1;
while (!Result && (Index <= Command.Length()))
{
int Len;
wchar_t APatternCmd;
GetToken(Command, Index, Len, APatternCmd);
if (((PatternCmd != L'!') && (PatternCmd == APatternCmd)) ||
((PatternCmd == L'!') && (Len == 1) && (APatternCmd != TEXT_TOKEN)))
{
Result = true;
}
Index += Len;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommand::ValidatePattern(const UnicodeString & /*Command*/,
int /*Index*/, int /*Len*/, wchar_t /*PatternCmd*/, void * /*Arg*/)
{
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TInteractiveCustomCommand::TInteractiveCustomCommand(
TCustomCommand * ChildCustomCommand)
{
FChildCustomCommand = ChildCustomCommand;
}
//---------------------------------------------------------------------------
void __fastcall TInteractiveCustomCommand::Prompt(
const UnicodeString & /*Prompt*/, UnicodeString & Value)
{
Value = L"";
}
//---------------------------------------------------------------------------
void __fastcall TInteractiveCustomCommand::Execute(
const UnicodeString & /*Command*/, UnicodeString & Value)
{
Value = L"";
}
//---------------------------------------------------------------------------
int __fastcall TInteractiveCustomCommand::PatternLen(const UnicodeString & Command, int Index)
{
int Len;
switch (Command[Index + 1])
{
case L'?':
{
const wchar_t * Ptr = Command.c_str() + Index - 1;
const wchar_t * PatternEnd = wcschr(Ptr + 1, L'!');
if (PatternEnd == NULL)
{
throw Exception(FMTLOAD(CUSTOM_COMMAND_UNTERMINATED, (Command[Index + 1], Index)));
}
Len = PatternEnd - Ptr + 1;
}
break;
case L'`':
{
const wchar_t * Ptr = Command.c_str() + Index - 1;
const wchar_t * PatternEnd = wcschr(Ptr + 2, L'`');
if (PatternEnd == NULL)
{
throw Exception(FMTLOAD(CUSTOM_COMMAND_UNTERMINATED, (Command[Index + 1], Index)));
}
Len = PatternEnd - Ptr + 1;
}
break;
default:
Len = FChildCustomCommand->PatternLen(Command, Index);
break;
}
return Len;
}
//---------------------------------------------------------------------------
bool __fastcall TInteractiveCustomCommand::PatternReplacement(const UnicodeString & Pattern,
UnicodeString & Replacement, bool & Delimit)
{
bool Result;
if ((Pattern.Length() >= 3) && (Pattern[2] == L'?'))
{
UnicodeString PromptStr;
int Pos = Pattern.SubString(3, Pattern.Length() - 2).Pos(L"?");
if (Pos > 0)
{
Replacement = Pattern.SubString(3 + Pos, Pattern.Length() - 3 - Pos);
if ((Pos > 1) && (Pattern[3 + Pos - 2] == L'\\'))
{
Delimit = false;
Pos--;
}
PromptStr = Pattern.SubString(3, Pos - 1);
}
else
{
PromptStr = Pattern.SubString(3, Pattern.Length() - 3);
}
Prompt(PromptStr, Replacement);
Result = true;
}
else if ((Pattern.Length() >= 3) && (Pattern[2] == L'`'))
{
UnicodeString Command = Pattern.SubString(3, Pattern.Length() - 3);
Command = FChildCustomCommand->Complete(Command, true);
Execute(Command, Replacement);
Delimit = false;
Result = true;
}
else
{
Result = false;
}
return Result;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TCustomCommandData::TCustomCommandData()
{
}
//---------------------------------------------------------------------------
__fastcall TCustomCommandData::TCustomCommandData(TTerminal * Terminal)
{
Init(Terminal->SessionData, Terminal->Password);
}
//---------------------------------------------------------------------------
__fastcall TCustomCommandData::TCustomCommandData(
TSessionData * SessionData, const UnicodeString & Password)
{
Init(SessionData, Password);
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandData::Init(
TSessionData * SessionData, const UnicodeString & APassword)
{
HostName = SessionData->HostNameExpanded;
UserName = SessionData->UserNameExpanded;
Password = APassword;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TFileCustomCommand::TFileCustomCommand()
{
}
//---------------------------------------------------------------------------
TFileCustomCommand::TFileCustomCommand(const TCustomCommandData & Data,
const UnicodeString & Path)
{
FData = Data;
FPath = Path;
}
//---------------------------------------------------------------------------
TFileCustomCommand::TFileCustomCommand(const TCustomCommandData & Data,
const UnicodeString & Path, const UnicodeString & FileName,
const UnicodeString & FileList) :
TCustomCommand()
{
FData = Data;
FPath = Path;
FFileName = FileName;
FFileList = FileList;
}
//---------------------------------------------------------------------------
int __fastcall TFileCustomCommand::PatternLen(const UnicodeString & Command, int Index)
{
int Len;
switch (toupper(Command[Index + 1]))
{
case L'@':
case L'U':
case L'P':
case L'/':
case L'&':
Len = 2;
break;
default:
Len = 1;
break;
}
return Len;
}
//---------------------------------------------------------------------------
bool __fastcall TFileCustomCommand::PatternReplacement(
const UnicodeString & Pattern, UnicodeString & Replacement, bool & Delimit)
{
// keep consistent with TSessionLog::OpenLogFile
if (Pattern == L"!@")
{
Replacement = FData.HostName;
}
else if (AnsiSameText(Pattern, L"!u"))
{
Replacement = FData.UserName;
}
else if (AnsiSameText(Pattern, L"!p"))
{
Replacement = FData.Password;
}
else if (Pattern == L"!/")
{
Replacement = UnixIncludeTrailingBackslash(FPath);
}
else if (Pattern == L"!&")
{
Replacement = FFileList;
// already delimited
Delimit = false;
}
else
{
assert(Pattern.Length() == 1);
Replacement = FFileName;
}
return true;
}
//---------------------------------------------------------------------------
void __fastcall TFileCustomCommand::Validate(const UnicodeString & Command)
{
int Found[2] = { 0, 0 };
CustomValidate(Command, &Found);
if ((Found[0] > 0) && (Found[1] > 0))
{
throw Exception(FMTLOAD(CUSTOM_COMMAND_FILELIST_ERROR,
(Found[1], Found[0])));
}
}
//---------------------------------------------------------------------------
void __fastcall TFileCustomCommand::ValidatePattern(const UnicodeString & Command,
int Index, int /*Len*/, wchar_t PatternCmd, void * Arg)
{
int * Found = static_cast<int *>(Arg);
assert(Index > 0);
if (PatternCmd == L'&')
{
Found[0] = Index;
}
else if ((PatternCmd != TEXT_TOKEN) && (PatternLen(Command, Index) == 1))
{
Found[1] = Index;
}
}
//---------------------------------------------------------------------------
bool __fastcall TFileCustomCommand::IsFileListCommand(const UnicodeString & Command)
{
return FindPattern(Command, L'&');
}
//---------------------------------------------------------------------------
bool __fastcall TFileCustomCommand::IsFileCommand(const UnicodeString & Command)
{
return FindPattern(Command, L'!') || FindPattern(Command, L'&');
}
//---------------------------------------------------------------------------
bool __fastcall TFileCustomCommand::IsSiteCommand(const UnicodeString & Command)
{
return FindPattern(Command, L'@');
}
//---------------------------------------------------------------------------
bool __fastcall TFileCustomCommand::IsPasswordCommand(const UnicodeString & Command)
{
return FindPattern(Command, L'p');
}
//---------------------------------------------------------------------------