1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/HierarchicalStorage.cpp
Eduardo C. a8fc40bd0e Portability: let core build with clang on Linux and Windows (#79)
* Cast between closure types with a functional cast, not reinterpret_cast

A Borland __closure is a built-in (code, data) pair, so reinterpret_cast
between two closure types is allowed. Under standard C++ a closure is a
class, and reinterpret_cast cannot convert one class object into another.
The functional cast is the old C-style cast this line had before
"Refactoring old-style casts" (e5efed895), so C++Builder compiles it as it
did then, and a compiler implementing closures as classes can give it the
same meaning.

* Read the knocked FTP port's IPv4 address as 32 bits

The address is read through an unsigned long pointer. That is 32 bits on
Windows (LLP64), so it reads exactly the four address bytes there; on LP64
targets unsigned long is 64 bits, and the read runs four bytes past the
address into whatever follows it in gethostbyname's buffer. The assignment
to the 32-bit s_addr then keeps the right bytes on little-endian machines,
which is why it goes unnoticed, but the over-read is undefined. ULONG is 32
bits on every Windows compiler, as Windows API layers for POSIX define it
too.

* Compile the synchronize checklist's Explorer file operation on Windows only

TSynchronizeChecklistFileOperation carries out a synchronization checklist
through Explorer's IFileOperation (a Shell COM object), so that local
deletions go to the recycle bin with Explorer's progress and undo. Only
WinSCP's Windows GUI uses it (CustomScpExplorer.cpp). Elsewhere neither the
object nor the interfaces exist, and declaring them only to compile would
pretend to a facility that is not there; a frontend on another platform
implements the operation its own way. The class and its implementation are
guarded with _WIN32, which C++Builder defines, so the Windows build is
unchanged.

* Declare the SIOCATMARK result as ULONG, the type p_ioctlsocket takes

network.c declares ioctlsocket as taking ULONG * (its DECL_WINDOWS_FUNCTION)
but passes a u_long. Both are 32 bits on Windows, so the two agree there. On
an LP64 platform u_long is 64 bits and ULONG 32, so the pointer types differ
and the callee would write half the variable. ULONG is what the declaration
says, and is the same type on Windows.

* Pick aes-ni.c's _mm_setr_epi8 for any GCC-compatible compiler

MM_SETR_EPI8 uses _mm_setr_epi8 only under _WIN64, falling back to a brace
initialiser that is correct for MSVC's __m128i (a byte union) but fills
clang's and GCC's (a two-lane vector) wrongly; the _WIN64 condition fixed
that for Win64 builds only. Any compiler defining __GNUC__ (clang does) now
takes the intrinsic.

* Compile WinSCP's Windows-only additions to neon on Windows only

WinSCP builds neon for Win32 alone, and three of its additions assume it:
ne_openssl.c includes <windows.h> (it uses nothing from it), timed_connect
switches the socket to non-blocking mode with Winsock's ioctlsocket instead
of upstream's fcntl, and ne_sock_sspi_init calls into SSPI unconditionally.
On POSIX neon uses BSD sockets directly, so the include is guarded with
_WIN32, the two ioctlsocket branches are taken only on _WIN32 (upstream's
fcntl code beside them is used otherwise), and ne_sock_sspi_init calls SSPI
under HAVE_SSPI, as ne_sock_init's own call does, reporting failure (-1)
without it -- which WebDAVFileSystem.cpp already handles as SSPI being
unavailable. C++Builder defines _WIN32 and neon's Win32 config.h defines
HAVE_SSPI, so the Windows build is unchanged.

* Use in_addr's s_addr in FileZilla's proxy layer

AsyncProxySocketLayer.cpp reaches the IPv4 address through Winsock's union,
sin_addr.S_un.S_addr. Winsock also defines s_addr as exactly that member
(#define s_addr S_un.S_addr), and s_addr is the name BSD sockets and POSIX
give the field; a POSIX in_addr has no S_un. Same field, so the Windows
build is unchanged.

* Pass FileZilla's two tool strings as UnicodeString, not std::wstring

CFileZillaTools::GetClientString and CustomReason return std::wstring; the
rest of the interface between core and FileZilla, and both sides' own code,
use UnicodeString or wchar_t pointers. Built with a 2-byte wchar_t on a
platform whose C++ library assumes 4 (clang's -fshort-wchar on Linux),
std::wstring is unusable: libstdc++ provides basic_string<wchar_t> as a
precompiled extern template for its own wchar_t, and char_traits<wchar_t>
calls the C library's 4-byte wcslen and wmemcpy: a Linux build of
FtpFileSystem.cpp imported wcslen through it. The values are only built from
UnicodeString, compared with empty() and read with c_str(), so UnicodeString
is a direct replacement (IsEmpty for empty). On Windows, where wchar_t is 2
bytes throughout, behaviour is unchanged.

* Build storage key paths with backslashes on every platform

THierarchicalStorage joins registry and INI key paths (Sessions\My site)
through its IncludeTrailingBackslash and ExcludeTrailingBackslash members,
which defer to the RTL functions of those names. The RTL's add and strip the
platform's path delimiter: a backslash on Windows, a slash on POSIX, where
these functions also build local file paths. Keys are not file paths: their
separator is a backslash everywhere, and the INI file's sections say so. The
members now add and strip a backslash themselves; on Windows that is what
the RTL did, so the behaviour there is unchanged. Without this, on POSIX the
current key became "Sessions/", matched no section, and no stored site
loaded.

* Hand neon a copy of the TLS-init closure, not its two pointers

SetNeonTlsInit stores the closure in the neon session by reinterpreting it
as a TMethod and saving its code and data pointers, and ne_init_ssl_session
rebuilds it with MakeMethod. That relies on a closure being exactly those
two pointers, which only Borland's __closure is; with any other closure type
the saved words are its internals, read from a by-value parameter that is
gone by the time neon calls back, and the rebuilt closure is garbage (a
clang build's first WebDAV-over-TLS connection crashed there). The session
now holds a heap copy of the closure, freed by neon's destroy-session hook
-- which also covers the sessions libs3 creates and destroys itself -- and
ne_init_ssl_session calls that copy. On C++Builder the behaviour is the
same, at the cost of one small allocation per session.

* Take CString's search results as const wchar_t *

CString::Find, ReverseFind and FindOneOf search the const string c_str()
returns and keep the result in a wchar_t *. ISO C++ declares wcschr,
wcsrchr, wcsstr and wcspbrk on a const string as returning const wchar_t *,
as MSVC's and newer mingw-w64 headers (llvm-mingw's) do, so the
initialisation does not compile there. The pointers are only compared and
subtracted: const is all they need.

* Index the string a string-list property returns, not the property

GetLocales takes a character of Exts->Strings[Index]. Under clang the
__declspec(property) standing for TStrings::Strings' __property reads a
second subscript as the property's own second index, so the expression is a
two-argument call to the getter and does not compile (parentheses do not
change that). Read into a UnicodeString first, the second subscript indexes
the string the property returns, as under C++Builder.

---------

Co-authored-by: Eduardo Correia <ecorreia@apliant.com.br>
2026-10-20 16:01:54 +02:00

2116 lines
65 KiB
C++

//---------------------------------------------------------------------------
#include <CorePCH.h>
#pragma hdrstop
#include "Exceptions.h"
#include "PuttyIntf.h"
#include "HierarchicalStorage.h"
//---------------------------------------------------------------------------
// ValueExists test was probably added to avoid registry exceptions when debugging
#define READ_REGISTRY(Method) \
if (FRegistry->ValueExists(Name)) \
try { return FRegistry->Method(Name); } catch(...) { return Default; } \
else return Default;
#define WRITE_REGISTRY(Method) \
try { FRegistry->Method(Name, Value); } catch(...) { }
//---------------------------------------------------------------------------
UnicodeString PuttyEscape(const RawByteString & Source, bool Value)
{
strbuf * sb = strbuf_new();
escape_registry_key(Source.c_str(), sb);
RawByteString Dest(sb->s);
strbuf_free(sb);
if (Value)
{
// We do not want to munge * in PasswordMask
Dest = ReplaceStr(Dest, L"%2A", L"*");
}
return UnicodeString(Dest.c_str(), Dest.Length());
}
//---------------------------------------------------------------------------
bool ToUnicode(const UnicodeString & Str, RawByteString & Utf)
{
Utf = RawByteString(UTF8String(Str));
bool Result = (Utf.Length() > Str.Length());
if (Result)
{
Utf.Insert(Bom, 1);
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall MungeStr(const UnicodeString & Str, bool ForceAnsi, bool Value)
{
RawByteString Source;
if (ForceAnsi)
{
Source = RawByteString(PuttyStr(Str));
}
else
{
ToUnicode(Str, Source);
}
return PuttyEscape(Source, Value);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall UnMungeStr(const UnicodeString & Str)
{
// Str should contain ASCII characters only
RawByteString Source = AnsiString(Str);
strbuf * sb = strbuf_new();
unescape_registry_key(Source.c_str(), sb);
RawByteString Dest(sb->s);
strbuf_free(sb);
UnicodeString Result;
if (Dest.SubString(1, Bom.Length()) == Bom)
{
Dest.Delete(1, Bom.Length());
Result = UTF8ToString(Dest);
}
else
{
Result = AnsiToString(Dest);
}
return Result;
}
//---------------------------------------------------------------------------
AnsiString PuttyStr(const UnicodeString & Str)
{
return AnsiString(Str);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall PuttyMungeStr(const UnicodeString & Str)
{
return MungeStr(Str, true, false);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall MungeIniName(const UnicodeString & Str, bool ForceAnsi)
{
bool NeedEscaping = false;
for (int Index = 1; !NeedEscaping && (Index <= Str.Length()); Index++)
{
wchar_t Ch = Str[Index];
NeedEscaping = IsWideChar(Ch) || (Ch == L'=');
}
UnicodeString Result;
if (!ForceAnsi && NeedEscaping)
{
RawByteString Utf;
ToUnicode(Str, Utf);
Result = PuttyEscape(Utf, false);
}
else
{
Result = Str;
}
Result = ReplaceStr(Result, L"=", L"%3D");
return Result;
}
//---------------------------------------------------------------------------
static UnicodeString EscapedBom(TraceInitStr(PuttyEscape(Bom, false)));
//---------------------------------------------------------------------------
UnicodeString __fastcall UnMungeIniName(const UnicodeString & Str)
{
UnicodeString Result;
if (StartsStr(EscapedBom, Str))
{
Result = UnMungeStr(Str);
}
else
{
// Backward compatibility only, with versions that did not Unicode-encoded strings with =.
// Can be dropped eventually.
int P = Str.Pos(L"%3D");
if (P > 0)
{
Result = ReplaceStr(Str, L"%3D", L"=");
}
else
{
Result = Str;
}
}
return Result;
}
//---------------------------------------------------------------------------
template<typename T>
void AddIntMapping(TIntMapping & Mapping, const wchar_t * Name, const T & Value)
{
if (Name != NULL)
{
Mapping.insert(std::make_pair(UnicodeString(Name), Value));
}
}
//---------------------------------------------------------------------------
template<typename T>
TIntMapping CreateIntMapping(
const wchar_t * Name1, const T & Value1,
const wchar_t * Name2 = NULL, const T & Value2 = T(0),
const wchar_t * Name3 = NULL, const T & Value3 = T(0))
{
TIntMapping Result;
AddIntMapping(Result, Name1, Value1);
AddIntMapping(Result, Name2, Value2);
AddIntMapping(Result, Name3, Value3);
return Result;
}
//---------------------------------------------------------------------------
TIntMapping CreateIntMappingFromEnumNames(const UnicodeString & ANames)
{
UnicodeString Names(ANames);
TIntMapping Result;
int Index = 0;
while (!Names.IsEmpty())
{
UnicodeString Name = CutToChar(Names, L';', true);
Result.insert(std::make_pair(Name, Index));
Index++;
}
return Result;
}
//---------------------------------------------------------------------------
TIntMapping AutoSwitchMapping = CreateIntMapping(L"on", asOn, L"off", asOff, L"auto", asAuto);
TIntMapping AutoSwitchReversedMapping = CreateIntMapping(L"on", asOff, L"off", asOn, L"auto", asAuto);
static TIntMapping BoolMapping = CreateIntMapping(L"on", true, L"off", false);
//===========================================================================
static UnicodeString AccessValueName(L"Access");
static UnicodeString DefaultAccessString(L"inherit");
//---------------------------------------------------------------------------
__fastcall THierarchicalStorage::THierarchicalStorage(const UnicodeString & AStorage)
{
FStorage = AStorage;
AccessMode = smRead;
Explicit = false;
ForceSave = false;
// While this was implemented in 5.0 already, for some reason
// it was disabled (by mistake?). So although enabled for 5.6.1 only,
// data written in Unicode/UTF8 can be read by all versions back to 5.0.
ForceAnsi = false;
MungeStringValues = true;
FFakeReadOnlyOpens = 0;
FRootAccess = -1;
}
//---------------------------------------------------------------------------
__fastcall THierarchicalStorage::~THierarchicalStorage()
{
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::Flush()
{
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::SetAccessMode(TStorageAccessMode value)
{
FAccessMode = value;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::GetCurrentSubKeyMunged()
{
if (!FKeyHistory.empty())
{
return FKeyHistory.back().Key;
}
else
{
return UnicodeString();
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::GetCurrentSubKey()
{
return UnMungeStr(GetCurrentSubKeyMunged());
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::ConfigureForPutty()
{
MungeStringValues = false;
ForceAnsi = true;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::OpenRootKey(bool CanCreate)
{
// This do not seem to be doing what it advertises.
// It probably "works" only when used as a first "Open". So let's verify that by this assertion.
DebugAssert(CurrentSubKey.IsEmpty());
return OpenSubKey(UnicodeString(), CanCreate);
}
//---------------------------------------------------------------------------
bool THierarchicalStorage::MungingKeyName(const UnicodeString & Key)
{
UnicodeString K = CurrentSubKey + Key + L"\\";
return UnmungedRoot.IsEmpty() || !SameText(LeftStr(UnmungedRoot + L"\\", K.Length()), K);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::MungeKeyName(const UnicodeString & Key)
{
UnicodeString Result;
if (!MungingKeyName(Key))
{
Result = Key;
}
else
{
Result = MungeStr(Key, ForceAnsi, false);
// if there's already ANSI-munged subkey, keep ANSI munging
if ((Result != Key) && !ForceAnsi && CanRead() && DoKeyExists(Key, true))
{
Result = MungeStr(Key, true, false);
}
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString THierarchicalStorage::MungeIniName(const UnicodeString & Str)
{
UnicodeString Result = ::MungeIniName(Str, ForceAnsi);
// This does not handle Ansi-encoded value with equal sign, but that's an edge case
if ((Result != Str) && !ForceAnsi && CanRead() && DoValueExists(Str, true))
{
Result = ::MungeIniName(Str, true);
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::DoReadRootAccessString()
{
UnicodeString Result;
if (OpenRootKey(false))
{
Result = ReadAccessString();
CloseSubKey();
}
else
{
Result = DefaultAccessString;
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::ReadAccessString()
{
UnicodeString Result;
if (!FKeyHistory.empty())
{
Result = ReadString(AccessValueName, DefaultAccessString);
}
else
{
Result = DoReadRootAccessString();
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall THierarchicalStorage::ReadAccess(unsigned int CurrentAccess)
{
UnicodeString Access = ReadAccessString();
unsigned int Result = 0;
while (!Access.IsEmpty())
{
UnicodeString Token = CutToChar(Access, L',', true);
if (SameText(Token, L"inherit"))
{
Result |= CurrentAccess;
}
else if (SameText(Token, "read"))
{
Result |= hsaRead;
}
else if (SameText(Token, "full"))
{
Result |= hsaRead | hsaWrite;
}
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall THierarchicalStorage::GetCurrentAccess()
{
unsigned int Result;
if (!FKeyHistory.empty())
{
// We must have resolved root access when opening the sub key the latest.
DebugAssert(FRootAccess >= 0);
Result = FKeyHistory.back().Access;
}
else
{
if (FRootAccess < 0)
{
FRootAccess = hsaRead; // Prevent recursion to allow reading the access
FRootAccess = ReadAccess(hsaRead | hsaWrite);
}
Result = FRootAccess;
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::OpenSubKeyPath(const UnicodeString & KeyPath, bool CanCreate)
{
DebugAssert(!KeyPath.IsEmpty() && (KeyPath[KeyPath.Length()] != L'\\'));
bool Result = false; // shut up
UnicodeString Buf(KeyPath);
int Opens = 0;
while (!Buf.IsEmpty())
{
UnicodeString SubKey = CutToChar(Buf, L'\\', false);
Result = OpenSubKey(SubKey, CanCreate);
if (Result)
{
Opens++;
}
}
if (Result)
{
FKeyHistory.back().Levels = Opens;
}
else
{
while (Opens > 0)
{
CloseSubKey();
Opens--;
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::OpenSubKey(const UnicodeString & Key, bool CanCreate)
{
UnicodeString MungedKey = MungeKeyName(Key);
bool Result;
unsigned int InheritAccess;
unsigned int Access;
// For the first open, CanWrite > GetCurrentAccess > ReadAccess has a (needed) side effect of caching root access.
if (!CanWrite() && CanCreate && !KeyExists(MungedKey))
{
InheritAccess = Access = 0; // do not even try to read the access, as the key is actually not opened
FFakeReadOnlyOpens++;
Result = true;
}
else
{
Access = hsaRead; // allow reading the access
InheritAccess = GetCurrentAccess();
Result = DoOpenSubKey(MungedKey, CanCreate);
}
if (Result)
{
TKeyEntry Entry;
Entry.Key = IncludeTrailingBackslash(CurrentSubKey + MungedKey);
Entry.Levels = 1;
Entry.Access = Access;
FKeyHistory.push_back(Entry);
// Read the real access only now, that the key is finally opened (it's in FKeyHistory)
FKeyHistory.back().Access = ReadAccess(InheritAccess);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::CloseSubKeyPath()
{
if (FKeyHistory.empty())
{
throw Exception(UnicodeString());
}
int Levels = FKeyHistory.back().Levels;
FKeyHistory.back().Levels = 1; // to satisfy the assertion in CloseSubKey()
while (Levels > 0)
{
CloseSubKey();
Levels--;
DebugAssert((Levels == 0) || (FKeyHistory.back().Levels == 1));
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::CloseSubKey()
{
if (FKeyHistory.empty())
{
throw Exception(UnicodeString());
}
DebugAssert(FKeyHistory.back().Levels == 1);
FKeyHistory.pop_back();
if (FFakeReadOnlyOpens > 0)
{
FFakeReadOnlyOpens--;
}
else
{
DoCloseSubKey();
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::CloseAll()
{
while (!CurrentSubKey.IsEmpty())
{
CloseSubKeyPath();
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::ClearSubKeys()
{
std::unique_ptr<TStringList> SubKeys(new TStringList());
GetSubKeyNames(SubKeys.get());
for (int Index = 0; Index < SubKeys->Count; Index++)
{
RecursiveDeleteSubKey(SubKeys->Strings[Index]);
}
}
//---------------------------------------------------------------------------
bool THierarchicalStorage::RecursiveDeleteSubKey(const UnicodeString & Key)
{
return DeleteSubKey(Key, true);
}
//---------------------------------------------------------------------------
bool THierarchicalStorage::DeleteSubKey(const UnicodeString & Key, bool Recursive)
{
bool CanWriteParent = CanWrite();
bool Result = OpenSubKey(Key, false);
if (Result)
{
if (Recursive)
{
ClearSubKeys();
// Cannot delete the key itself, but can delete its contents, so at least delete the values
// (which would otherwise be deleted implicitly by DoDeleteSubKey)
if (!CanWriteParent && CanWrite())
{
ClearValues();
}
}
std::unique_ptr<TStrings> ValueNames(new TStringList());
if (!Recursive)
{
GetValueNames(ValueNames.get());
}
// Only if all subkeys were successfully deleted in ClearSubKeys
Result = CanWriteParent && !HasSubKeys() && (ValueNames->Count == 0);
CloseSubKey();
if (Result)
{
Result = DoDeleteSubKey(Key);
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::HasSubKeys()
{
std::unique_ptr<TStrings> SubKeys(new TStringList());
GetSubKeyNames(SubKeys.get());
bool Result = (SubKeys->Count > 0);
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::DeleteValue(const UnicodeString & Name)
{
if (CanWrite())
{
return DoDeleteValue(Name);
}
else
{
return false;
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::KeyExists(const UnicodeString & SubKey)
{
if (CanRead())
{
return DoKeyExists(SubKey, ForceAnsi);
}
else
{
return false;
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::ValueExists(const UnicodeString & Value)
{
if (CanRead())
{
return DoValueExists(Value, ForceAnsi);
}
else
{
return false;
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::ReadValues(TStrings * Strings, bool MaintainKeys)
{
std::unique_ptr<TStrings> Names(new TStringList());
GetValueNames(Names.get());
for (int Index = 0; Index < Names->Count; Index++)
{
if (MaintainKeys)
{
Strings->Add(FORMAT(L"%s=%s", (Names->Strings[Index], ReadString(Names->Strings[Index], UnicodeString()))));
}
else
{
Strings->Add(ReadString(Names->Strings[Index], UnicodeString()));
}
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::ClearValues()
{
std::unique_ptr<TStrings> Names(new TStringList());
GetValueNames(Names.get());
for (int Index = 0; Index < Names->Count; Index++)
{
DeleteValue(Names->Strings[Index]);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteValues(TStrings * Strings, bool MaintainKeys)
{
ClearValues();
if (Strings != NULL)
{
for (int Index = 0; Index < Strings->Count; Index++)
{
if (MaintainKeys)
{
DebugAssert(Strings->Strings[Index].Pos(L"=") > 1);
WriteString(Strings->Names[Index], Strings->Values[Strings->Names[Index]]);
}
else
{
WriteString(IntToStr(Index), Strings->Strings[Index]);
}
}
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::HasAccess(unsigned int Access)
{
return
FLAGSET(GetCurrentAccess(), Access) &&
// There should never be any kind of access to a non-existent key
DebugAlwaysTrue(FFakeReadOnlyOpens == 0);
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::CanRead()
{
return HasAccess(hsaRead);
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::GetSubKeyNames(TStrings * Strings)
{
if (CanRead())
{
DoGetSubKeyNames(Strings);
}
else
{
Strings->Clear();
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::GetValueNames(TStrings * Strings)
{
if (CanRead())
{
DoGetValueNames(Strings);
int Index = 0;
while (Index < Strings->Count)
{
if (SameText(Strings->Strings[Index], AccessValueName))
{
Strings->Delete(Index);
}
else
{
Index++;
}
}
}
else
{
Strings->Clear();
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::ReadBool(const UnicodeString & Name, bool Default)
{
if (CanRead())
{
return DoReadBool(Name, Default);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
int __fastcall THierarchicalStorage::ReadInteger(const UnicodeString & Name, int Default)
{
return ReadIntegerWithMapping(Name, Default, NULL);
}
//---------------------------------------------------------------------------
int THierarchicalStorage::ReadIntegerWithMapping(const UnicodeString & Name, int Default, const TIntMapping * Mapping)
{
if (CanRead())
{
return DoReadInteger(Name, Default, Mapping);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
__int64 __fastcall THierarchicalStorage::ReadInt64(const UnicodeString & Name, __int64 Default)
{
if (CanRead())
{
return DoReadInt64(Name, Default);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
TDateTime __fastcall THierarchicalStorage::ReadDateTime(const UnicodeString & Name, TDateTime Default)
{
if (CanRead())
{
return DoReadDateTime(Name, Default);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
double __fastcall THierarchicalStorage::ReadFloat(const UnicodeString & Name, double Default)
{
if (CanRead())
{
return DoReadFloat(Name, Default);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::ReadStringRaw(const UnicodeString & Name, const UnicodeString & Default)
{
if (CanRead())
{
return DoReadStringRaw(Name, Default);
}
else
{
return Default;
}
}
//---------------------------------------------------------------------------
size_t __fastcall THierarchicalStorage::ReadBinaryData(const UnicodeString & Name, void * Buffer, size_t Size)
{
if (CanRead())
{
return DoReadBinaryData(Name, Buffer, Size);
}
else
{
return 0;
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::ReadString(const UnicodeString & Name, const UnicodeString & Default)
{
UnicodeString Result;
if (CanRead())
{
if (MungeStringValues)
{
Result = UnMungeStr(ReadStringRaw(Name, MungeStr(Default, ForceAnsi, true)));
}
else
{
Result = ReadStringRaw(Name, Default);
}
}
else
{
Result = Default;
}
return Result;
}
//---------------------------------------------------------------------------
size_t __fastcall THierarchicalStorage::BinaryDataSize(const UnicodeString & Name)
{
if (CanRead())
{
return DoBinaryDataSize(Name);
}
else
{
return 0;
}
}
//---------------------------------------------------------------------------
RawByteString __fastcall THierarchicalStorage::ReadBinaryData(const UnicodeString & Name)
{
int Size = SizeToIntChecked(BinaryDataSize(Name));
RawByteString Value;
Value.SetLength(Size);
ReadBinaryData(Name, Value.c_str(), Size);
return Value;
}
//---------------------------------------------------------------------------
RawByteString __fastcall THierarchicalStorage::ReadStringAsBinaryData(const UnicodeString & Name, const RawByteString & Default)
{
UnicodeString UnicodeDefault = AnsiToString(Default);
// This should be exactly the same operation as calling ReadString in
// C++Builder 6 (non-Unicode) on Unicode-based OS
// (conversion is done by Ansi layer of the OS)
UnicodeString String = ReadString(Name, UnicodeDefault);
AnsiString Ansi = AnsiString(String);
RawByteString Result = RawByteString(Ansi.c_str(), Ansi.Length());
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::CanWrite()
{
return HasAccess(hsaWrite);
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteBool(const UnicodeString & Name, bool Value)
{
if (CanWrite())
{
DoWriteBool(Name, Value);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteStringRaw(const UnicodeString & Name, const UnicodeString & Value)
{
if (CanWrite())
{
DoWriteStringRaw(Name, Value);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteInteger(const UnicodeString & Name, int Value)
{
if (CanWrite())
{
DoWriteInteger(Name, Value);
}
}
//---------------------------------------------------------------------------
// Simple alias to WriteInteger
void __fastcall THierarchicalStorage::WriteEnum(const UnicodeString & Name, int Value)
{
WriteInteger(Name, Value);
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteInt64(const UnicodeString & Name, __int64 Value)
{
if (CanWrite())
{
DoWriteInt64(Name, Value);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteDateTime(const UnicodeString & Name, TDateTime Value)
{
if (CanWrite())
{
// TRegistry.WriteDateTime does this internally
DoWriteBinaryData(Name, &Value, sizeof(Value));
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteFloat(const UnicodeString & Name, double Value)
{
if (CanWrite())
{
// TRegistry.WriteFloat does this internally
DoWriteBinaryData(Name, &Value, sizeof(Value));
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteString(const UnicodeString & Name, const UnicodeString & Value)
{
if (MungeStringValues)
{
WriteStringRaw(Name, MungeStr(Value, ForceAnsi, true));
}
else
{
WriteStringRaw(Name, Value);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteBinaryData(const UnicodeString & Name, const void * Buffer, int Size)
{
if (CanWrite())
{
DoWriteBinaryData(Name, Buffer, Size);
}
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteBinaryData(const UnicodeString & Name, const RawByteString & Value)
{
WriteBinaryData(Name, Value.c_str(), Value.Length());
}
//---------------------------------------------------------------------------
void __fastcall THierarchicalStorage::WriteBinaryDataAsString(const UnicodeString & Name, const RawByteString & Value)
{
// This should be exactly the same operation as calling WriteString in
// C++Builder 6 (non-Unicode) on Unicode-based OS
// (conversion is done by Ansi layer of the OS)
WriteString(Name, AnsiToString(Value));
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::IncludeTrailingBackslash(const UnicodeString & S)
{
// expanded from ?: as it caused memory leaks
if (S.IsEmpty())
{
return S;
}
else
{
// Keys are separated by backslashes on all platforms, unlike file paths
return (S[S.Length()] == L'\\') ? S : (S + L'\\');
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall THierarchicalStorage::ExcludeTrailingBackslash(const UnicodeString & S)
{
// expanded from ?: as it caused memory leaks
if (S.IsEmpty())
{
return S;
}
else
{
return (S[S.Length()] == L'\\') ? S.SubString(1, S.Length() - 1) : S;
}
}
//---------------------------------------------------------------------------
bool __fastcall THierarchicalStorage::GetTemporary()
{
return false;
}
//===========================================================================
__fastcall TRegistryStorage::TRegistryStorage(const UnicodeString & AStorage) :
THierarchicalStorage(IncludeTrailingBackslash(AStorage))
{
FWowMode = 0;
Init();
};
//---------------------------------------------------------------------------
__fastcall TRegistryStorage::TRegistryStorage(const UnicodeString & AStorage, HKEY ARootKey, REGSAM WowMode):
THierarchicalStorage(IncludeTrailingBackslash(AStorage))
{
FWowMode = WowMode;
Init();
FRegistry->RootKey = ARootKey;
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::Init()
{
FRegistry = new TRegistry();
FRegistry->Access = KEY_READ | FWowMode;
}
//---------------------------------------------------------------------------
__fastcall TRegistryStorage::~TRegistryStorage()
{
delete FRegistry;
};
//---------------------------------------------------------------------------
// Used only in OpenSessionInPutty
bool __fastcall TRegistryStorage::Copy(TRegistryStorage * Storage)
{
TRegistry * Registry = Storage->FRegistry;
bool Result = true;
std::unique_ptr<TStrings> Names(new TStringList());
Registry->GetValueNames(Names.get());
std::vector<unsigned char> Buffer(1024, 0);
int Index = 0;
while ((Index < Names->Count) && Result)
{
UnicodeString Name = MungeStr(Names->Strings[Index], ForceAnsi, false);
unsigned long Size = SizeToUIntChecked(Buffer.size());
unsigned long Type;
int RegResult;
do
{
RegResult = RegQueryValueEx(Registry->CurrentKey, Name.c_str(), NULL, &Type, &Buffer[0], &Size);
if (RegResult == ERROR_MORE_DATA)
{
Buffer.resize(Size);
}
} while (RegResult == ERROR_MORE_DATA);
Result = (RegResult == ERROR_SUCCESS);
if (Result)
{
RegResult = RegSetValueEx(FRegistry->CurrentKey, Name.c_str(), NULL, Type, &Buffer[0], Size);
Result = (RegResult == ERROR_SUCCESS);
}
++Index;
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TRegistryStorage::GetSource()
{
return RootKeyToStr(FRegistry->RootKey) + L"\\" + Storage;
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::SetAccessMode(TStorageAccessMode value)
{
THierarchicalStorage::SetAccessMode(value);
if (FRegistry)
{
switch (AccessMode) {
case smRead:
FRegistry->Access = KEY_READ | FWowMode;
break;
case smReadWrite:
default:
FRegistry->Access = KEY_READ | KEY_WRITE | FWowMode;
break;
}
}
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoOpenSubKey(const UnicodeString & SubKey, bool CanCreate)
{
UnicodeString PrevPath;
bool WasOpened = (FRegistry->CurrentKey != NULL);
if (WasOpened)
{
PrevPath = FRegistry->CurrentPath;
DebugAssert(SamePaths(PrevPath, Storage + GetCurrentSubKeyMunged()));
FRegistry->CloseKey();
}
UnicodeString K = ExcludeTrailingBackslash(Storage + CurrentSubKey + SubKey);
bool Result = FRegistry->OpenKey(K, CanCreate);
if (!Result && WasOpened)
{
FRegistry->OpenKey(PrevPath, false);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoCloseSubKey()
{
FRegistry->CloseKey();
if (!FKeyHistory.empty())
{
FRegistry->OpenKey(Storage + GetCurrentSubKeyMunged(), True);
}
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoDeleteSubKey(const UnicodeString & SubKey)
{
UnicodeString K;
if (FKeyHistory.empty())
{
K = Storage + CurrentSubKey;
}
K += MungeKeyName(SubKey);
return FRegistry->DeleteKey(K);
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoGetSubKeyNames(TStrings * Strings)
{
FRegistry->GetKeyNames(Strings);
for (int Index = 0; Index < Strings->Count; Index++)
{
Strings->Strings[Index] = UnMungeStr(Strings->Strings[Index]);
}
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoGetValueNames(TStrings * Strings)
{
FRegistry->GetValueNames(Strings);
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoDeleteValue(const UnicodeString & Name)
{
return FRegistry->DeleteValue(Name);
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoKeyExists(const UnicodeString & SubKey, bool AForceAnsi)
{
UnicodeString K = SubKey;
if (MungingKeyName(K))
{
K = MungeStr(K, AForceAnsi, false);
}
bool Result = FRegistry->KeyExists(K);
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoValueExists(const UnicodeString & Value, bool DebugUsedArg(AForceAnsi))
{
// TODO: use AForceAnsi
bool Result = FRegistry->ValueExists(Value);
return Result;
}
//---------------------------------------------------------------------------
size_t __fastcall TRegistryStorage::DoBinaryDataSize(const UnicodeString & Name)
{
size_t Result = FRegistry->GetDataSize(Name);
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TRegistryStorage::DoReadBool(const UnicodeString & Name, bool Default)
{
READ_REGISTRY(ReadBool);
}
//---------------------------------------------------------------------------
TDateTime __fastcall TRegistryStorage::DoReadDateTime(const UnicodeString & Name, TDateTime Default)
{
// Internally does what would DoReadBinaryData do (like in DoReadInt64)
READ_REGISTRY(ReadDateTime);
}
//---------------------------------------------------------------------------
double __fastcall TRegistryStorage::DoReadFloat(const UnicodeString & Name, double Default)
{
// Internally does what would DoReadBinaryData do (like in DoReadInt64)
READ_REGISTRY(ReadFloat);
}
//---------------------------------------------------------------------------
int __fastcall TRegistryStorage::DoReadInteger(const UnicodeString & Name, int Default, const TIntMapping *)
{
READ_REGISTRY(ReadInteger);
}
//---------------------------------------------------------------------------
__int64 __fastcall TRegistryStorage::DoReadInt64(const UnicodeString & Name, __int64 Default)
{
__int64 Result;
if (DoReadBinaryData(Name, &Result, sizeof(Result)) == 0)
{
Result = Default;
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TRegistryStorage::DoReadStringRaw(const UnicodeString & Name, const UnicodeString & Default)
{
READ_REGISTRY(ReadString);
}
//---------------------------------------------------------------------------
size_t __fastcall TRegistryStorage::DoReadBinaryData(const UnicodeString & Name, void * Buffer, size_t Size)
{
int Result;
if (FRegistry->ValueExists(Name))
{
try
{
Result = FRegistry->ReadBinaryData(Name, Buffer, SizeToIntChecked(Size));
}
catch(...)
{
Result = 0;
}
}
else
{
Result = 0;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoWriteBool(const UnicodeString & Name, bool Value)
{
WRITE_REGISTRY(WriteBool);
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoWriteStringRaw(const UnicodeString & Name, const UnicodeString & Value)
{
WRITE_REGISTRY(WriteString);
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoWriteInteger(const UnicodeString & Name, int Value)
{
WRITE_REGISTRY(WriteInteger);
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoWriteInt64(const UnicodeString & Name, __int64 Value)
{
WriteBinaryData(Name, &Value, sizeof(Value));
}
//---------------------------------------------------------------------------
void __fastcall TRegistryStorage::DoWriteBinaryData(const UnicodeString & Name, const void * Buffer, int Size)
{
try
{
FRegistry->WriteBinaryData(Name, const_cast<void *>(Buffer), Size);
}
catch(...)
{
}
}
//===========================================================================
__fastcall TCustomIniFileStorage::TCustomIniFileStorage(const UnicodeString & Storage, TCustomIniFile * IniFile) :
THierarchicalStorage(Storage),
FIniFile(IniFile),
FMasterStorageOpenFailures(0),
FOpeningSubKey(false)
{
}
//---------------------------------------------------------------------------
__fastcall TCustomIniFileStorage::~TCustomIniFileStorage()
{
delete FIniFile;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomIniFileStorage::GetSource()
{
return Storage;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomIniFileStorage::GetCurrentSection()
{
return ExcludeTrailingBackslash(GetCurrentSubKeyMunged());
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::CacheSections()
{
if (FSections.get() == NULL)
{
FSections.reset(new TStringList());
FIniFile->ReadSections(FSections.get());
FSections->Sorted = true; // has to set only after reading as ReadSections reset it to false
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::ResetCache()
{
FSections.reset(NULL);
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::SetAccessMode(TStorageAccessMode value)
{
if (FMasterStorage.get() != NULL)
{
FMasterStorage->AccessMode = value;
}
THierarchicalStorage::SetAccessMode(value);
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoKeyExistsInternal(const UnicodeString & SubKey)
{
CacheSections();
bool Result = false;
UnicodeString NewKey = ExcludeTrailingBackslash(CurrentSubKey + SubKey);
if (FSections->Count > 0)
{
int Index = -1;
Result = FSections->Find(NewKey, Index);
if (!Result &&
(Index < FSections->Count) &&
(FSections->Strings[Index].SubString(1, NewKey.Length()+1) == NewKey + L"\\"))
{
Result = true;
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoOpenSubKey(const UnicodeString & SubKey, bool CanCreate)
{
bool Result =
CanCreate ||
DoKeyExistsInternal(SubKey);
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::OpenSubKey(const UnicodeString & Key, bool CanCreate)
{
bool Result;
// To cache root access in advance, otherwise we end up calling ourselves, what TAutoFlag does not like
GetCurrentAccess();
{
TAutoFlag Flag(FOpeningSubKey);
Result = THierarchicalStorage::OpenSubKey(Key, CanCreate);
}
if (FMasterStorage.get() != NULL)
{
if (FMasterStorageOpenFailures > 0)
{
if (Result)
{
FMasterStorageOpenFailures++;
}
}
else
{
bool MasterResult = FMasterStorage->OpenSubKey(Key, CanCreate);
if (!Result && MasterResult)
{
Result = THierarchicalStorage::OpenSubKey(Key, true);
DebugAssert(Result);
}
else if (Result && !MasterResult)
{
FMasterStorageOpenFailures++;
}
}
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoCloseSubKey()
{
// noop
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::CloseSubKey()
{
// What we are called to restore previous key from OpenSubKey,
// when opening path component fails, the master storage was not involved yet
if (!FOpeningSubKey && (FMasterStorage.get() != NULL))
{
if (FMasterStorageOpenFailures > 0)
{
FMasterStorageOpenFailures--;
}
else
{
FMasterStorage->CloseSubKey();
}
}
THierarchicalStorage::CloseSubKey();
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoDeleteSubKey(const UnicodeString & SubKey)
{
bool Result = true;
try
{
ResetCache();
FIniFile->EraseSection(CurrentSubKey + MungeKeyName(SubKey));
}
catch (...)
{
}
if (HandleByMasterStorage())
{
Result =
FMasterStorage->CanWrite() &&
FMasterStorage->DoDeleteSubKey(SubKey);
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoGetSubKeyNames(TStrings * Strings)
{
Strings->Clear();
if (HandleByMasterStorage())
{
FMasterStorage->GetSubKeyNames(Strings);
}
CacheSections();
for (int i = 0; i < FSections->Count; i++)
{
UnicodeString Section = FSections->Strings[i];
if (AnsiCompareText(CurrentSubKey,
Section.SubString(1, CurrentSubKey.Length())) == 0)
{
UnicodeString SubSection = Section.SubString(CurrentSubKey.Length() + 1,
Section.Length() - CurrentSubKey.Length());
int P = SubSection.Pos(L"\\");
if (P)
{
SubSection.SetLength(P - 1);
}
if (Strings->IndexOf(SubSection) < 0)
{
Strings->Add(UnMungeStr(SubSection));
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoGetValueNames(TStrings * Strings)
{
if (HandleByMasterStorage())
{
FMasterStorage->GetValueNames(Strings);
}
FIniFile->ReadSection(CurrentSection, Strings);
for (int Index = 0; Index < Strings->Count; Index++)
{
UnicodeString S = Strings->Strings[Index];
S = UnMungeIniName(S);
Strings->Strings[Index] = S;
}
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoKeyExists(const UnicodeString & SubKey, bool AForceAnsi)
{
return
(HandleByMasterStorage() && FMasterStorage->DoKeyExists(SubKey, AForceAnsi)) ||
DoKeyExistsInternal(MungeStr(SubKey, AForceAnsi, false));
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoValueExistsInternal(const UnicodeString & Value, bool AForceAnsi)
{
return FIniFile->ValueExists(CurrentSection, ::MungeIniName(Value, AForceAnsi));
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoValueExists(const UnicodeString & Value, bool AForceAnsi)
{
return
(HandleByMasterStorage() && FMasterStorage->DoValueExists(Value, AForceAnsi)) ||
DoValueExistsInternal(Value, AForceAnsi);
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoDeleteValue(const UnicodeString & Name)
{
bool Result = true;
if (HandleByMasterStorage())
{
Result = FMasterStorage->DeleteValue(Name);
}
ResetCache();
FIniFile->DeleteKey(CurrentSection, MungeIniName(Name));
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::HandleByMasterStorage()
{
return
(FMasterStorage.get() != NULL) &&
(FMasterStorageOpenFailures == 0);
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::HandleReadByMasterStorage(const UnicodeString & Name)
{
return HandleByMasterStorage() && !DoValueExistsInternal(Name, ForceAnsi);
}
//---------------------------------------------------------------------------
size_t __fastcall TCustomIniFileStorage::DoBinaryDataSize(const UnicodeString & Name)
{
if (HandleReadByMasterStorage(Name))
{
return FMasterStorage->BinaryDataSize(Name);
}
else
{
return ReadStringRaw(Name, L"").Length() / 2;
}
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::DoReadBool(const UnicodeString & Name, bool Default)
{
if (HandleReadByMasterStorage(Name))
{
return FMasterStorage->ReadBool(Name, Default);
}
else
{
int IntDefault = int(Default);
int Int = DoReadIntegerWithMapping(Name, IntDefault, &BoolMapping);
return (Int != 0);
}
}
//---------------------------------------------------------------------------
int __fastcall TCustomIniFileStorage::DoReadIntegerWithMapping(const UnicodeString & Name, int Default, const TIntMapping * Mapping)
{
int Result = 0; // shut up
bool ReadAsInteger = true;
UnicodeString MungedName = MungeIniName(Name);
if (Mapping != NULL) // optimization
{
UnicodeString S = FIniFile->ReadString(CurrentSection, MungedName, EmptyStr);
if (!S.IsEmpty())
{
S = S.LowerCase();
TIntMapping::const_iterator I = Mapping->find(S);
if (I != Mapping->end())
{
Result = I->second;
ReadAsInteger = false;
}
}
}
if (ReadAsInteger)
{
Result = FIniFile->ReadInteger(CurrentSection, MungedName, Default);
}
return Result;
}
//---------------------------------------------------------------------------
int __fastcall TCustomIniFileStorage::DoReadInteger(const UnicodeString & Name, int Default, const TIntMapping * Mapping)
{
int Result;
if (HandleReadByMasterStorage(Name))
{
Result = FMasterStorage->ReadIntegerWithMapping(Name, Default, Mapping);
}
else
{
Result = DoReadIntegerWithMapping(Name, Default, Mapping);
}
return Result;
}
//---------------------------------------------------------------------------
__int64 __fastcall TCustomIniFileStorage::DoReadInt64(const UnicodeString & Name, __int64 Default)
{
__int64 Result;
if (HandleReadByMasterStorage(Name))
{
Result = FMasterStorage->ReadInt64(Name, Default);
}
else
{
Result = Default;
UnicodeString Str;
Str = ReadStringRaw(Name, L"");
if (!Str.IsEmpty())
{
Result = StrToInt64Def(Str, Default);
}
}
return Result;
}
//---------------------------------------------------------------------------
TDateTime __fastcall TCustomIniFileStorage::DoReadDateTime(const UnicodeString & Name, TDateTime Default)
{
TDateTime Result;
if (HandleReadByMasterStorage(Name))
{
Result = FMasterStorage->ReadDateTime(Name, Default);
}
else
{
UnicodeString Value = FIniFile->ReadString(CurrentSection, MungeIniName(Name), L"");
if (Value.IsEmpty())
{
Result = Default;
}
else
{
try
{
RawByteString Raw = HexToBytes(Value);
if (static_cast<size_t>(Raw.Length()) == sizeof(Result))
{
memcpy(&Result, Raw.c_str(), sizeof(Result));
}
else
{
Result = StrToDateTime(Value);
}
}
catch(...)
{
Result = Default;
}
}
}
return Result;
}
//---------------------------------------------------------------------------
double __fastcall TCustomIniFileStorage::DoReadFloat(const UnicodeString & Name, double Default)
{
double Result;
if (HandleReadByMasterStorage(Name))
{
Result = FMasterStorage->ReadFloat(Name, Default);
}
else
{
UnicodeString Value = FIniFile->ReadString(CurrentSection, MungeIniName(Name), L"");
if (Value.IsEmpty())
{
Result = Default;
}
else
{
try
{
RawByteString Raw = HexToBytes(Value);
if (static_cast<size_t>(Raw.Length()) == sizeof(Result))
{
memcpy(&Result, Raw.c_str(), sizeof(Result));
}
else
{
Result = static_cast<double>(StrToFloat(Value));
}
}
catch(...)
{
Result = Default;
}
}
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomIniFileStorage::DoReadStringRaw(const UnicodeString & Name, const UnicodeString & Default)
{
UnicodeString Result;
if (HandleReadByMasterStorage(Name))
{
Result = FMasterStorage->ReadStringRaw(Name, Default);
}
else
{
Result = FIniFile->ReadString(CurrentSection, MungeIniName(Name), Default);
}
return Result;
}
//---------------------------------------------------------------------------
size_t __fastcall TCustomIniFileStorage::DoReadBinaryData(const UnicodeString & Name, void * Buffer, size_t Size)
{
size_t Len;
if (HandleReadByMasterStorage(Name))
{
Size = FMasterStorage->ReadBinaryData(Name, Buffer, Size);
}
else
{
RawByteString Value = HexToBytes(ReadStringRaw(Name, L""));
Len = Value.Length();
if (Size > Len)
{
Size = Len;
}
DebugAssert(Buffer);
memcpy(Buffer, Value.c_str(), Size);
}
return Size;
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteBool(const UnicodeString & Name, bool Value)
{
if (HandleByMasterStorage())
{
FMasterStorage->WriteBool(Name, Value);
}
ResetCache();
FIniFile->WriteBool(CurrentSection, MungeIniName(Name), Value);
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteInteger(const UnicodeString & Name, int Value)
{
if (HandleByMasterStorage())
{
FMasterStorage->WriteInteger(Name, Value);
}
ResetCache();
FIniFile->WriteInteger(CurrentSection, MungeIniName(Name), Value);
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteInt64(const UnicodeString & Name, __int64 Value)
{
if (HandleByMasterStorage())
{
FMasterStorage->WriteInt64(Name, Value);
}
DoWriteStringRawInternal(Name, IntToStr(Value));
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteStringRawInternal(const UnicodeString & Name, const UnicodeString & Value)
{
ResetCache();
FIniFile->WriteString(CurrentSection, MungeIniName(Name), Value);
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteStringRaw(const UnicodeString & Name, const UnicodeString & Value)
{
if (HandleByMasterStorage())
{
FMasterStorage->WriteStringRaw(Name, Value);
}
DoWriteStringRawInternal(Name, Value);
}
//---------------------------------------------------------------------------
void __fastcall TCustomIniFileStorage::DoWriteBinaryData(const UnicodeString & Name, const void * Buffer, int Size)
{
if (HandleByMasterStorage())
{
FMasterStorage->WriteBinaryData(Name, Buffer, Size);
}
DoWriteStringRawInternal(Name, BytesToHex(RawByteString(static_cast<const char*>(Buffer), Size)));
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomIniFileStorage::DoReadRootAccessString()
{
UnicodeString Result;
if (OpenSubKey(L"_", false))
{
Result = ReadAccessString();
CloseSubKey();
}
else
{
Result = DefaultAccessString;
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall TCustomIniFileStorage::GetCurrentAccess()
{
unsigned int Result;
// The way THierarchicalStorage::OpenSubKey is implemented, the access will be zero for non-existing keys in
// configuration overrides => delegating access handling to the master storage (which still should read overriden access keys)
if (FMasterStorage.get() != NULL)
{
Result = FMasterStorage->GetCurrentAccess();
}
else
{
Result = THierarchicalStorage::GetCurrentAccess();
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomIniFileStorage::HasAccess(unsigned int Access)
{
bool Result;
// Avoid the FFakeReadOnlyOpens assertion in THierarchicalStorage::HasAccess, which is not valid for overriden configuration
if (HandleByMasterStorage())
{
Result = FMasterStorage->HasAccess(Access);
}
else
{
Result = THierarchicalStorage::HasAccess(Access);
}
return Result;
}
//===========================================================================
TIniFileStorage * __fastcall TIniFileStorage::CreateFromPath(const UnicodeString & AStorage)
{
// The code was originally inline in the parent constructor call in the TIniFileStorage::TIniFileStorage [public originally].
// But if the TMemIniFile constructor throws (e.g. because the INI file is locked), the exception causes access violation.
// Moving the code to a factory solves this.
TMemIniFile * IniFile = new TMemIniFile(AStorage);
return new TIniFileStorage(AStorage, IniFile);
}
//---------------------------------------------------------------------------
TIniFileStorage * __fastcall TIniFileStorage::CreateNul()
{
// Before we passed "nul", but we have report of a system, where opening "nul" file fails.
// Passing an empty string is even more efficient, as it does not even try to read the file.
TMemIniFile * IniFile = new TMemIniFile(EmptyStr);
return new TIniFileStorage(INI_NUL, IniFile);
}
//---------------------------------------------------------------------------
__fastcall TIniFileStorage::TIniFileStorage(const UnicodeString & AStorage, TCustomIniFile * IniFile):
TCustomIniFileStorage(AStorage, IniFile)
{
if (!FIniFile->FileName.IsEmpty())
{
FOriginal = new TStringList();
dynamic_cast<TMemIniFile *>(FIniFile)->GetStrings(FOriginal);
}
else
{
FOriginal = NULL;
}
ApplyOverrides();
}
//---------------------------------------------------------------------------
void __fastcall TIniFileStorage::Flush()
{
if (FMasterStorage.get() != NULL)
{
FMasterStorage->Flush();
}
// This does not seem correct, if storage is written after it is flushed (though we probably never do that currently)
if ((FOriginal != NULL) && !FIniFile->FileName.IsEmpty())
{
std::unique_ptr<TStrings> Strings(new TStringList);
std::unique_ptr<TStrings> Original(FOriginal);
FOriginal = NULL;
dynamic_cast<TMemIniFile *>(FIniFile)->GetStrings(Strings.get());
if (!Strings->Equals(Original.get()))
{
int Attr;
// preserve attributes (especially hidden)
bool Exists = FileExists(ApiPath(Storage));
if (Exists)
{
Attr = GetFileAttributes(ApiPath(Storage).c_str());
}
else
{
Attr = FILE_ATTRIBUTE_NORMAL;
}
if (FLAGSET(Attr, FILE_ATTRIBUTE_READONLY) && ForceSave)
{
SetFileAttributes(ApiPath(Storage).c_str(), Attr & ~FILE_ATTRIBUTE_READONLY);
}
HANDLE Handle;
int Error;
bool Retry;
int Trying = 0;
do
{
Error = 0;
Handle =
CreateFile(ApiPath(Storage).c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, Attr, 0);
if (Handle == INVALID_HANDLE_VALUE)
{
Error = GetLastError();
}
Retry = (Error == ERROR_SHARING_VIOLATION) && (Trying < 2000);
if (Retry)
{
const int Step = 100;
Sleep(Step);
Trying += Step;
}
}
while (Retry);
if (Handle == INVALID_HANDLE_VALUE)
{
// "access denied" errors upon implicit saves to existing file are ignored
if (Explicit || !Exists || (Error != ERROR_ACCESS_DENIED))
{
throw EOSExtException(FMTLOAD((Exists ? WRITE_ERROR : CREATE_FILE_ERROR), (Storage)));
}
}
else
{
try
{
std::unique_ptr<TStream> Stream(new TSafeHandleStream(int(Handle)));
try
{
Strings->SaveToStream(Stream.get());
}
__finally
{
CloseHandle(Handle);
}
}
catch (Exception & E)
{
throw ExtException(&E, FMTLOAD(WRITE_ERROR, (Storage)));
}
}
}
}
}
//---------------------------------------------------------------------------
__fastcall TIniFileStorage::~TIniFileStorage()
{
Flush();
}
//---------------------------------------------------------------------------
void __fastcall TIniFileStorage::ApplyOverrides()
{
UnicodeString OverridesKey = IncludeTrailingBackslash(L"Override");
CacheSections();
for (int i = 0; i < FSections->Count; i++)
{
UnicodeString Section = FSections->Strings[i];
if (SameText(OverridesKey,
Section.SubString(1, OverridesKey.Length())))
{
UnicodeString SubKey = Section.SubString(OverridesKey.Length() + 1,
Section.Length() - OverridesKey.Length());
// this all uses raw names (munged)
TStrings * Names = new TStringList;
try
{
FIniFile->ReadSection(Section, Names);
for (int ii = 0; ii < Names->Count; ii++)
{
UnicodeString Name = Names->Strings[ii];
UnicodeString Value = FIniFile->ReadString(Section, Name, L"");
FIniFile->WriteString(SubKey, Name, Value);
}
}
__finally
{
delete Names;
}
FIniFile->EraseSection(Section);
ResetCache();
}
}
}
//===========================================================================
enum TWriteMode { wmAllow, wmFail, wmIgnore };
//---------------------------------------------------------------------------
class TOptionsIniFile : public TCustomIniFile
{
public:
__fastcall TOptionsIniFile(TStrings * Options, TWriteMode WriteMode, const UnicodeString & RootKey);
virtual UnicodeString __fastcall ReadString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Default);
virtual void __fastcall WriteString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Value);
virtual void __fastcall ReadSection(const UnicodeString Section, TStrings * Strings);
virtual void __fastcall ReadSections(TStrings* Strings);
virtual void __fastcall ReadSectionValues(const UnicodeString Section, TStrings* Strings);
virtual void __fastcall EraseSection(const UnicodeString Section);
virtual void __fastcall DeleteKey(const UnicodeString Section, const UnicodeString Ident);
virtual void __fastcall UpdateFile();
// Hoisted overload
void __fastcall ReadSections(const UnicodeString Section, TStrings* Strings);
// Ntb, we can implement ValueExists more efficiently than the TCustomIniFile.ValueExists
private:
TStrings * FOptions;
TWriteMode FWriteMode;
UnicodeString FRootKey;
bool __fastcall AllowWrite();
bool __fastcall AllowSection(const UnicodeString & Section);
UnicodeString __fastcall FormatKey(const UnicodeString & Section, const UnicodeString & Ident);
};
//---------------------------------------------------------------------------
__fastcall TOptionsIniFile::TOptionsIniFile(TStrings * Options, TWriteMode WriteMode, const UnicodeString & RootKey) :
TCustomIniFile(UnicodeString())
{
FOptions = Options;
FWriteMode = WriteMode;
FRootKey = RootKey;
if (!FRootKey.IsEmpty())
{
FRootKey += PathDelim;
}
}
//---------------------------------------------------------------------------
bool __fastcall TOptionsIniFile::AllowWrite()
{
switch (FWriteMode)
{
case wmAllow:
return true;
case wmFail:
NotImplemented();
UNREACHABLE_AFTER_NORETURN(return false);
case wmIgnore:
return false;
default:
DebugFail();
return false;
}
}
//---------------------------------------------------------------------------
bool __fastcall TOptionsIniFile::AllowSection(const UnicodeString & Section)
{
UnicodeString Name = Section;
if (!Name.IsEmpty())
{
Name += PathDelim;
}
bool Result = SameText(Name.SubString(1, FRootKey.Length()), FRootKey);
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TOptionsIniFile::FormatKey(const UnicodeString & Section, const UnicodeString & Ident)
{
UnicodeString Result = Section;
if (!Result.IsEmpty())
{
Result += PathDelim;
}
Result += Ident; // Can be empty, when called from a constructor, AllowSection or ReadSection
if (DebugAlwaysTrue(AllowSection(Section)))
{
Result.Delete(1, FRootKey.Length());
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TOptionsIniFile::ReadString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Default)
{
UnicodeString Value;
if (!AllowSection(Section))
{
Value = Default;
}
else
{
UnicodeString Name = FormatKey(Section, Ident);
int Index = FOptions->IndexOfName(Name);
if (Index >= 0)
{
Value = FOptions->ValueFromIndex[Index];
}
else
{
Value = Default;
}
}
return Value;
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::WriteString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Value)
{
if (AllowWrite() &&
DebugAlwaysTrue(AllowSection(Section)))
{
UnicodeString Name = FormatKey(Section, Ident);
SetStringValueEvenIfEmpty(FOptions, Name, Value);
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::ReadSection(const UnicodeString Section, TStrings * Strings)
{
if (AllowSection(Section))
{
UnicodeString SectionPrefix = FormatKey(Section, UnicodeString());
Strings->BeginUpdate();
try
{
for (int Index = 0; Index < FOptions->Count; Index++)
{
UnicodeString Name = FOptions->Names[Index];
if (SameText(Name.SubString(1, SectionPrefix.Length()), SectionPrefix) &&
(LastDelimiter(PathDelim, Name) <= SectionPrefix.Length()))
{
Strings->Add(Name.SubString(SectionPrefix.Length() + 1, Name.Length() - SectionPrefix.Length()));
}
}
}
__finally
{
Strings->EndUpdate();
}
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::ReadSections(TStrings * Strings)
{
std::unique_ptr<TStringList> Sections(CreateSortedStringList());
for (int Index = 0; Index < FOptions->Count; Index++)
{
UnicodeString Name = FOptions->Names[Index];
int P = LastDelimiter(PathDelim, Name);
if (P > 0)
{
UnicodeString Section = Name.SubString(1, P - 1);
if (Sections->IndexOf(Section) < 0)
{
Sections->Add(Section);
}
}
}
for (int Index = 0; Index < Sections->Count; Index++)
{
Strings->Add(FRootKey + Sections->Strings[Index]);
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::ReadSectionValues(const UnicodeString Section, TStrings * /*Strings*/)
{
DebugUsedParam(Section);
NotImplemented();
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::EraseSection(const UnicodeString Section)
{
DebugUsedParam(Section);
if (AllowWrite())
{
NotImplemented();
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::DeleteKey(const UnicodeString Section, const UnicodeString Ident)
{
if (AllowWrite() &&
DebugAlwaysTrue(AllowSection(Section)))
{
UnicodeString Name = FormatKey(Section, Ident);
int Index = FOptions->IndexOfName(Name);
if (Index >= 0)
{
FOptions->Delete(Index);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::UpdateFile()
{
if (AllowWrite())
{
// noop
}
}
//---------------------------------------------------------------------------
void __fastcall TOptionsIniFile::ReadSections(const UnicodeString /*Section*/, TStrings * /*Strings*/)
{
NotImplemented();
}
//===========================================================================
__fastcall TOptionsStorage::TOptionsStorage(TStrings * Options, bool AllowWrite):
TCustomIniFileStorage(
UnicodeString(L"Command-line options"),
new TOptionsIniFile(Options, (AllowWrite ? wmAllow : wmFail), UnicodeString()))
{
}
//---------------------------------------------------------------------------
__fastcall TOptionsStorage::TOptionsStorage(TStrings * Options, const UnicodeString & RootKey, THierarchicalStorage * MasterStorage) :
TCustomIniFileStorage(
UnicodeString(L"Command-line options overriding " + MasterStorage->Source),
new TOptionsIniFile(Options, wmIgnore, RootKey))
{
FMasterStorage.reset(MasterStorage);
}
//---------------------------------------------------------------------------
bool __fastcall TOptionsStorage::GetTemporary()
{
return true;
}