diff --git a/Lgi_vs2019.vcxproj b/Lgi_vs2019.vcxproj
--- a/Lgi_vs2019.vcxproj
+++ b/Lgi_vs2019.vcxproj
@@ -1,471 +1,632 @@
Debug
x64
ReleaseNoOptimize
x64
Release
x64
Lgi
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}
10.0
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
<_ProjectFileVersion>12.0.30501.0
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64
.\Lib\
$(Platform)$(Configuration)19\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64d
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64nop
NDEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Release/Lgi.tlb
MinSpace
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
NDEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
MachineX64
_DEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Debug/Lgi.tlb
Disabled
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;LGI_LIBRARY;_DEBUG;WINDOWS;LGI_RES;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level3
true
ProgramDatabase
Default
+ false
_DEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
NotSet
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
false
$(OutDir)$(TargetName).lib
MachineX64
NDEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Release/Lgi.tlb
MinSpace
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
+ false
NDEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
MachineX64
false
true
true
false
false
false
true
true
true
true
true
true
false
false
false
true
true
true
true
true
true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lgi_vs2019.vcxproj.filters b/Lgi_vs2019.vcxproj.filters
--- a/Lgi_vs2019.vcxproj.filters
+++ b/Lgi_vs2019.vcxproj.filters
@@ -1,782 +1,1255 @@
{afe8cb77-9ad1-4536-bbdd-3c127e7ed08c}
cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
{66a64573-871b-4499-ae26-c19e9e2a514a}
{3fc23ef0-f144-4f1f-a9b4-18d3392bb63d}
{c6cd6d73-d33c-4413-ade1-9dad78e2dc9c}
{c8684fc7-2e3c-4f15-8284-9d44b044f6c6}
{87b1c801-b9ce-4f6c-97ab-a8f89aee9594}
{c06c25f2-2c07-4900-a517-4a6a324069e9}
{2c01a737-36cf-4197-bfa1-20395060263f}
{1e4cd802-8b94-4328-930e-37bfbfbedff5}
{01075698-dde2-4ed0-808f-7dd54414b597}
{4a6845a8-e6ec-47d5-8f6c-aa0cfdbc68df}
{f567a76b-edd5-434d-b0d9-d51481f34845}
{3dee0237-b01c-44e8-a730-08dc661df82f}
{bbaaace6-0ac7-4e76-90ae-9e5d5a0cb899}
{71e7b970-a070-40a7-a99f-88c215e14e44}
{6e115ea1-09fb-492b-82b6-428afe17fed9}
{719ef36f-419f-46f9-aef9-2f8158e4d106}
{fb221556-3700-4dd8-ba9a-10b5d66bfe54}
{8e9f0321-56ae-4485-a338-e87d714c7f50}
{c6050f41-574b-4a92-9ce5-860be2719ecf}
{a07cd969-801e-4ec9-9394-e34912a3271d}
{e067fae0-ef98-4e7a-8434-6f36590ba0e6}
{0a3e2151-b13a-407a-8cd9-ccb20f15cacf}
{c72248a4-c2f0-43b9-950d-89d09bfddbb3}
{dad60202-c763-4f32-9dfb-fe7def4637ee}
{532dfa4a-58d3-4133-9ed6-a9fbf7f1556e}
{5409aca4-2a55-4b2f-a719-d3db4e3cd7e4}
{e3a3aadd-47ef-4723-9bcc-7db1f75a18b0}
{c4327acf-78c3-4ef1-b8bc-3aac9ea52b41}
{b3c906b8-595e-4641-8eec-8ad03ab13f6f}
{baf0a65b-4a9c-4b11-850d-a957c19a22bf}
{6e349f5b-36a8-4821-9574-4040a3784204}
{ddfdebae-8bcf-4224-8938-2716aba03822}
{a126c55a-edee-489f-a852-25cbd1b433b2}
{ab5fd4a0-3862-42fd-b4ff-d5d8b0443f53}
{048d5e0a-220f-4911-999d-96965eb53691}
{258aef64-8cd0-4838-8131-147196656a99}
{814a5d81-3fd5-461b-a4a3-cda593ea404b}
{5fe450b8-5fa9-440e-9fb0-03860b3548d0}
h;hpp;hxx;hm;inl
Source Files\Core\Resources
Source Files\Core\Resources
Source Files\Core\Skin
Source Files\General\Hash
Source Files\General\Hash
Source Files\Graphics
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Dialogs
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Dialogs
Source Files\Dialogs
Source Files\Text
Source Files\Widgets\Native Windows
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Graphics\Font
Source Files\General\Clipboard
Source Files\Graphics
Source Files\Graphics\Colour Reduction
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Core\Memory Subsystem
Source Files\Text
Source Files\Text
Source Files\Core\DateTime
Source Files\Widgets\Native Windows
Source Files\Graphics\Font
Source Files\Dialogs
Source Files\Core\Drag and Drop
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Core\File
Source Files\Core\File
Source Files\Graphics\Filters
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics
Source Files\Graphics\Gdi Leak
Source Files\General
Source Files\Graphics
Source Files\Dialogs
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Libraries
Source Files\General
Source Files\General
Source Files\General
Source Files\Widgets\Xp
Source Files\Dialogs
Source Files\Text
Source Files\Core\Memory Subsystem
Source Files\Graphics\Surfaces
Source Files\Core\Memory Subsystem
Source Files\Core\Menus
Source Files\Core\Menus
Source Files\General
Source Files\General\Mru
Source Files\Core\Mutex
Source Files\Network
Source Files\Network
Source Files\General
Source Files\Core\File
Source Files\Widgets\Xp
Source Files\General
Source Files\Graphics
Source Files\Widgets\Xp
Source Files\Graphics\Surfaces
Source Files\Graphics
Source Files\Widgets\Native Windows
Source Files\Dialogs
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Graphics
Source Files\Graphics\Surfaces
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Memory Subsystem
Source Files\Text
Source Files\Widgets\Xp
Source Files\Core\Process
Source Files\Graphics\Surfaces
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Threads
Source Files\Core\Threads
Source Files\Core\Threads
Source Files\Text
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Graphics\Font
Source Files\Text
Source Files\Text
Source Files\Network
Source Files\Text
Source Files\Core\Variant
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Text
Source Files\Widgets\Xp
Source Files\Core\Resources
Source Files\Core\Resources
Source Files\General\Hash
Source Files\General\Hash
Source Files\Graphics
Source Files\Graphics\Gdi Leak
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
Source Files\Graphics\Font
Source Files\Interface
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
Header Files
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
Header Files
Header Files
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
\ No newline at end of file
diff --git a/include/lgi/common/Containers.h b/include/lgi/common/Containers.h
--- a/include/lgi/common/Containers.h
+++ b/include/lgi/common/Containers.h
@@ -1,1148 +1,1159 @@
/**
\file
\author Matthew Allen
\date 27/11/1996
\brief Container class header.\n
Copyright (C) 1996-2003, Matthew Allen
*/
#ifndef _CONTAIN_H_
#define _CONTAIN_H_
#include
#include "lgi/common/LgiInc.h"
#include "LgiOsDefs.h"
#include "lgi/common/Stream.h"
#include "lgi/common/Profile.h"
#include
/// Template for using DLinkList with a type safe API.
#define ITEM_PTRS 64
#define LGI_LIST_VALIDATION 0
LgiFunc bool UnitTest_ListClass();
#ifdef _DEBUG
#define VALIDATE() Validate()
#else
#define VALIDATE()
#endif
template
class List
{
struct LstBlk
{
LstBlk *Next, *Prev;
uint8_t Count;
T *Ptr[ITEM_PTRS];
LstBlk()
{
Next = Prev = NULL;
Count = 0;
ZeroObj(Ptr);
}
bool Full()
{
return Count >= ITEM_PTRS;
}
int Remaining()
{
return ITEM_PTRS - Count;
}
};
public:
class Iter
{
public:
const List *Lst;
LstBlk *i;
int Cur, Ver;
OsThreadId Thread;
bool CheckThread() const
{
#if 1
return true;
#else
auto Cur = GetCurrentThreadId();
bool Ok = Thread == Cur;
LAssert(Ok);
return Ok;
#endif
}
#ifdef _DEBUG
#define CHECK_THREAD CheckThread();
#else
#define CHECK_THREAD
#endif
Iter(const List *lst)
{
Lst = lst;
i = 0;
Cur = 0;
Ver = lst->Ver;
Thread = GetCurrentThreadId();
}
Iter(const List *lst, LstBlk *item, int c)
{
Lst = lst;
i = item;
Cur = c;
Ver = lst->Ver;
Thread = GetCurrentThreadId();
}
bool operator ==(const Iter &it) const
{
CHECK_THREAD
int x = (int)In() + (int)it.In();
if (x == 2)
return (i == it.i) && (Cur == it.Cur);
return x == 0;
}
bool operator !=(const Iter &it) const
{
CHECK_THREAD
return !(*this == it);
}
bool operator <(const Iter &it) const
{
CHECK_THREAD
return Cur < it.Cur;
}
bool operator <=(const Iter &it) const
{
CHECK_THREAD
return Cur <= it.Cur;
}
bool operator >(const Iter &it) const
{
CHECK_THREAD
return Cur > it.Cur;
}
bool operator >=(const Iter &it) const
{
CHECK_THREAD
return Cur >= it.Cur;
}
bool In() const
{
CHECK_THREAD
if (Ver != Lst->Ver)
{
if (!Lst->ValidBlock(i))
return false;
}
return i &&
Cur >= 0 &&
Cur < i->Count;
}
operator T*() const
{
CHECK_THREAD
return In() ? i->Ptr[Cur] : NULL;
}
T *operator *() const
{
CHECK_THREAD
return In() ? i->Ptr[Cur] : NULL;
}
Iter &operator =(LstBlk *item)
{
CHECK_THREAD
i = item;
if (!i)
Cur = 0;
return *this;
}
Iter &operator =(int c)
{
CHECK_THREAD
Cur = c;
return *this;
}
Iter &operator =(Iter *iter)
{
CHECK_THREAD
Lst = iter->Lst;
i = iter->i;
Cur = iter->Cur;
return *this;
}
int GetIndex(int Base)
{
CHECK_THREAD
if (i)
return Base + Cur;
return -1;
}
bool Next()
{
CHECK_THREAD
if (i)
{
if (!In())
return false;
Cur++;
if (Cur >= i->Count)
{
i = i->Next;
if (i)
{
Cur = 0;
return i->Count > 0;
}
}
else return true;
}
return false;
}
bool Prev()
{
CHECK_THREAD
if (i)
{
if (!In())
return false;
Cur--;
if (Cur < 0)
{
i = i->Prev;
if (i && i->Count > 0)
{
Cur = i->Count - 1;
return true;
}
}
else return true;
}
return false;
}
bool Delete()
{
CHECK_THREAD
if (i)
{
LAssert(Lst);
i->Delete(Cur, i);
return true;
}
return false;
}
Iter &operator ++() { Next(); return *this; }
Iter &operator --() { Prev(); return *this; }
Iter &operator ++(int) { Next(); return *this; }
Iter &operator --(int) { Prev(); return *this; }
};
typedef Iter I;
// typedef int (*CompareFn)(T *a, T *b, NativeInt data);
protected:
size_t Items;
int Ver;
LstBlk *FirstObj, *LastObj;
bool ValidBlock(LstBlk *b) const
{
for (LstBlk *i = FirstObj; i; i = i->Next)
if (i == b)
return true;
return false;
}
LstBlk *NewBlock(LstBlk *Where)
{
LstBlk *i = new LstBlk;
LAssert(i != NULL);
if (!i)
return NULL;
if (Where)
{
i->Prev = Where;
if (i->Prev->Next)
{
// Insert
i->Next = Where->Next;
i->Prev->Next = i->Next->Prev = i;
}
else
{
// Append
i->Prev->Next = i;
LAssert(LastObj == Where);
LastObj = i;
}
}
else
{
// First object
LAssert(FirstObj == 0);
LAssert(LastObj == 0);
FirstObj = LastObj = i;
}
return i;
}
bool DeleteBlock(LstBlk *i)
{
if (!i)
{
LAssert(!"No ptr.");
return false;
}
if (i->Prev != 0 && i->Next != 0)
{
LAssert(FirstObj != i);
LAssert(LastObj != i);
}
if (i->Prev)
{
i->Prev->Next = i->Next;
}
else
{
LAssert(FirstObj == i);
FirstObj = i->Next;
}
if (i->Next)
{
i->Next->Prev = i->Prev;
}
else
{
LAssert(LastObj == i);
LastObj = i->Prev;
}
delete i;
Ver++; // This forces the iterators to recheck their block ptr
return true;
}
bool Insert(LstBlk *i, T *p, ssize_t Index = -1)
{
if (!i)
return false;
if (i->Full())
{
if (!i->Next)
{
// Append a new LstBlk
if (!NewBlock(i))
return false;
}
if (Index < 0)
return Insert(i->Next, p, Index);
// Push last pointer into Next
if (i->Next->Full())
NewBlock(i); // Create an empty Next
if (!Insert(i->Next, i->Ptr[ITEM_PTRS-1], 0))
return false;
i->Count--;
Items--; // We moved the item... not inserted it.
// Fall through to the local "non-full" insert...
}
LAssert(!i->Full());
if (Index < 0)
Index = i->Count;
else if (Index < i->Count)
memmove(i->Ptr+Index+1, i->Ptr+Index, (i->Count-Index) * sizeof(p));
i->Ptr[Index] = p;
i->Count++;
Items++;
LAssert(i->Count <= ITEM_PTRS);
return true;
}
Iter GetIndex(size_t Index, size_t *Base = NULL) const
{
size_t n = 0;
for (LstBlk *i = FirstObj; i; i = i->Next)
{
if (Index >= n && Index < n + i->Count)
{
if (Base)
*Base = n;
return Iter(this, i, (int) (Index - n));
}
n += i->Count;
}
if (Base)
*Base = 0;
return Iter(this);
}
Iter GetPtr(T *Ptr, size_t *Base = NULL)
{
size_t n = 0;
for (LstBlk *i = FirstObj; i; i = i->Next)
{
for (int k=0; kCount; k++)
{
if (i->Ptr[k] == Ptr)
{
if (Base)
*Base = n;
return Iter(this, i, k);
}
}
n += i->Count;
}
if (Base)
*Base = 0;
return Iter(this);
}
public:
List()
{
FirstObj = LastObj = NULL;
Items = 0;
Ver = 0;
}
~List()
{
VALIDATE();
Empty();
}
size_t Length() const
{
return Items;
}
bool Length(size_t Len)
{
if (Len == 0)
return Empty();
else if (Len == Items)
return true;
VALIDATE();
bool Status = false;
if (Len < Items)
{
// Decrease list size...
size_t Base = 0;
Iter i = GetIndex(Len, &Base);
if (i.i)
{
size_t Offset = Len - Base;
if (!(Offset <= i.i->Count))
{
LAssert(!"Offset error");
}
i.i->Count = (uint8_t) (Len - Base);
LAssert(i.i->Count >= 0 && i.i->Count < ITEM_PTRS);
while (i.i->Next)
{
DeleteBlock(i.i->Next);
}
Items = Len;
}
else LAssert(!"Iterator invalid.");
}
else
{
// Increase list size...
LAssert(!"Impl me.");
}
VALIDATE();
return Status;
}
bool Empty()
{
VALIDATE();
LstBlk *n;
for (LstBlk *i = FirstObj; i; i = n)
{
n = i->Next;
delete i;
}
FirstObj = LastObj = NULL;
Items = 0;
Ver++;
VALIDATE();
return true;
}
bool DeleteAt(size_t i)
{
VALIDATE();
Iter p = GetIndex(i);
if (!p.In())
return false;
bool Status = Delete(p);
VALIDATE();
return Status;
}
bool Delete(Iter &Pos)
{
if (!Pos.In())
return false;
int &Index = Pos.Cur;
LstBlk *&i = Pos.i;
if (Index < i->Count-1)
memmove(i->Ptr+Index, i->Ptr+Index+1, (i->Count-Index-1) * sizeof(T*));
Items--;
if (--i->Count == 0)
{
// This Item is now empty, remove and reset current
// into the next Item
LstBlk *n = i->Next;
bool Status = DeleteBlock(i);
Pos.Cur = 0;
Pos.i = n;
return Status;
}
else if (Index >= i->Count)
{
// Carry current item over to next Item
Pos.i = Pos.i->Next;
Pos.Cur = 0;
}
return true;
}
bool Delete(T *Ptr)
{
VALIDATE();
Iter It = GetPtr(Ptr);
if (!It.In())
return false;
bool Status = Delete(It);
VALIDATE();
return Status;
}
bool Insert(T *p, ssize_t Index = -1)
{
VALIDATE();
if (!LastObj)
{
LstBlk *b = NewBlock(NULL);
if (!b)
return false;
b->Ptr[b->Count++] = p;
Items++;
VALIDATE();
return true;
}
bool Status;
size_t Base;
Iter Pos(this);
if (Index < 0)
Status = Insert(LastObj, p, Index);
else
{
Pos = GetIndex(Index, &Base);
if (Pos.i)
Status = Insert(Pos.i, p, (int) (Index - Base));
else
Status = Insert(LastObj, p, -1);
}
VALIDATE();
LAssert(Status);
return Status;
}
bool Add(T *p)
{
return Insert(p);
}
T *operator [](size_t Index) const
{
VALIDATE();
auto it = GetIndex(Index);
VALIDATE();
return it;
}
ssize_t IndexOf(T *p)
{
VALIDATE();
size_t Base = -1;
auto It = GetPtr(p, &Base);
LAssert(Base != -1);
ssize_t Idx = It.In() ? Base + It.Cur : -1;
VALIDATE();
return Idx;
}
bool HasItem(T *p)
{
VALIDATE();
Iter Pos = GetPtr(p);
bool Status = Pos.In();
VALIDATE();
return Status;
}
T *ItemAt(ssize_t i)
{
VALIDATE();
Iter It = GetIndex(i);
VALIDATE();
return It;
}
/// Sorts the list
template
void Sort
(
/// The callback function used to compare 2 pointers
int (*Compare)(T *a, T *b, User data),
/// User data that is passed into the callback
User Data = 0
)
{
if (Items < 1)
return;
VALIDATE();
// LProfile prof("ListSort");
// Save the List to an Array
LArray a;
a.Length(Length());
T **p = a.AddressOf();
for (LstBlk *i = FirstObj; i; i = i->Next)
for (int n=0; nCount; n++)
*p++ = i->Ptr[n];
// prof.Add("Compare");
struct UserData {
int (*Compare)(T *a, T *b, User data);
User Data;
} ud = {Compare, Data};
#if !defined(WINDOWS) && !defined(HAIKU) && !defined(LINUX)
#define USER_DATA_FIRST 1
#else
#define USER_DATA_FIRST 0
#endif
#if defined(WINDOWS)
/* _ACRTIMP void __cdecl qsort_s(void* _Base,
rsize_t _NumOfElements,
rsize_t _SizeOfElements,
int (__cdecl* _PtFuncCompare)(void*, void const*, void const*),
void* _Context); */
qsort_s
#else
qsort_r
#endif
(
a.AddressOf(),
a.Length(),
sizeof(T*),
#if USER_DATA_FIRST
&ud,
#endif
#if defined(HAIKU) || defined(LINUX)
// typedef int (*_compare_function_qsort_r)(const void*, const void*, void*);
// extern void qsort_r(void* base, size_t numElements, size_t sizeOfElement, _compare_function_qsort_r, void* cookie);
[](const void *a, const void *b, void *ud) -> int
#else
[](void *ud, const void *a, const void *b) -> int
#endif
{
auto *user = (UserData*)ud;
return user->Compare(*(T**)a, *(T**)b, user->Data);
}
#if !USER_DATA_FIRST
, &ud
#endif
);
// prof.Add("Copy");
// Copy back to the List
p = a.AddressOf();
for (LstBlk *i = FirstObj; i; i = i->Next)
for (int n=0; nCount; n++)
i->Ptr[n] = *p++;
VALIDATE();
}
/// Delete all pointers in the list as dynamically allocated objects
void DeleteObjects()
{
VALIDATE();
LstBlk *n;
for (LstBlk *i = FirstObj; i; i = n)
{
n = i->Next;
for (int n=0; nCount; n++)
{
if (i->Ptr[n])
{
#ifdef _DEBUG
size_t Objs = Items;
#endif
delete i->Ptr[n];
#ifdef _DEBUG
if (Objs != Items)
LAssert(!"Do you have self deleting objects?");
#endif
i->Ptr[n] = NULL;
}
}
delete i;
}
FirstObj = LastObj = NULL;
Items = 0;
Ver++;
VALIDATE();
}
/// Delete all pointers in the list as dynamically allocated arrays
void DeleteArrays()
{
VALIDATE();
LstBlk *n;
for (LstBlk *i = FirstObj; i; i = n)
{
n = i->Next;
for (int n=0; nCount; n++)
{
delete [] i->Ptr[n];
i->Ptr[n] = NULL;
}
delete i;
}
FirstObj = LastObj = NULL;
Items = 0;
Ver++;
VALIDATE();
}
void Swap(List &other)
{
LSwap(FirstObj, other.FirstObj);
LSwap(LastObj, other.LastObj);
LSwap(Items, other.Items);
LSwap(Ver, other.Ver);
}
/// Assign the contents of another list to this one
#if 0
List &operator=(const List &lst)
{
Empty();
for (auto i : lst)
Add(i);
return *this;
}
#else
List &operator =(const List &lst)
{
VALIDATE();
// Make sure we have enough blocks allocated
size_t i = 0;
// Set the existing blocks to empty...
for (LstBlk *out = FirstObj; out; out = out->Next)
{
out->Count = 0;
i += ITEM_PTRS;
}
// If we don't have enough, add more...
while (i < lst.Length())
{
LstBlk *out = NewBlock(LastObj);
if (out)
i += ITEM_PTRS;
else
{
LAssert(!"Can't allocate enough blocks?");
return *this;
}
}
// If we have too many, free some...
while (LastObj && i > lst.Length() + ITEM_PTRS)
{
DeleteBlock(LastObj);
i -= ITEM_PTRS;
}
// Now copy over the block's contents.
LstBlk *out = FirstObj;
Items = 0;
for (LstBlk *in = lst.FirstObj; in; in = in->Next)
{
for (int pos = 0; pos < in->Count; )
{
if (!out->Remaining())
{
out = out->Next;
if (!out)
{
LAssert(!"We should have pre-allocated everything...");
return *this;
}
}
int Cp = MIN(out->Remaining(), in->Count - pos);
LAssert(Cp > 0);
memcpy(out->Ptr + out->Count, in->Ptr + pos, Cp * sizeof(T*));
out->Count += Cp;
pos += Cp;
Items += Cp;
}
}
VALIDATE();
return *this;
}
#endif
Iter begin(ssize_t At = 0) { return GetIndex(At); }
Iter rbegin(ssize_t At = 0) { return GetIndex(Length()-1); }
Iter end() { return Iter(this, NULL, -1); }
bool Validate() const
{
if (FirstObj == NULL &&
LastObj == NULL &&
Items == 0)
return true;
#if LGI_LIST_VALIDATION
size_t n = 0;
LstBlk *Prev = NULL;
for (LstBlk *i = FirstObj; i; i = i->Next)
{
for (int k=0; kCount; k++)
{
if (!i->Ptr[k])
{
LAssert(!"NULL pointer in LstBlk.");
return false;
}
else
{
n++;
}
}
if (i == FirstObj)
{
if (i->Prev)
{
LAssert(!"First object's 'Prev' should be NULL.");
return false;
}
}
else if (i == LastObj)
{
if (i->Next)
{
LAssert(!"Last object's 'Next' should be NULL.");
return false;
}
}
else
{
if (i->Prev != Prev)
{
LAssert(!"Middle LstBlk 'Prev' incorrect.");
return false;
}
}
Prev = i;
}
if (Items != n)
{
LAssert(!"Item count cache incorrect.");
return false;
}
#endif
return true;
}
};
/// \brief Data storage class.
///
/// Allows data to be buffered in separate memory
/// blocks and then written to a continuous block for processing. Works
/// as a first in first out que. You can (and should) provide a suitable
/// PreAlloc size to the constructor. This can reduce the number of blocks
/// of memory being used (and their associated alloc/free time and
/// tracking overhead) in high volume situations.
class LgiClass LMemQueue : public LStream
{
protected:
/// Data block. These can contain a mix of 3 types of data:
/// 1) Bytes that have been read (always at the start of the block)
/// 2) Bytes that have been written (always in the middle)
/// 3) Unwritten buffer space (always at the end)
///
/// Initially a block starts out as completely type 3 bytes, just garbage
/// data waiting to be written to. Then something writes into the pipe and bytes
/// are stored into this free space, and the 'Used' variable is set to show
/// how much of the available buffer is used. At this point we have type 2 and
/// type 3 bytes in the block. The buffer can fill up completely in which
/// case Used == Size and there are no type 3 bytes left. Also at some point
/// something can start reading bytes out of the block which causes the 'Next'
/// value to be increased, at which point the block starts with 'Next' bytes
/// of type 1. Crystal?
struct Block
{
/// Number of bytes in this block that have been read
/// Type 1 or 'read' bytes are in [0,Next-1].
- int Next;
+ int Next = 0;
+
/// Number of bytes that are used in this block include read bytes.
/// Type 2 or 'used' bytes are in [Next,Used-1].
- int Used;
+ int Used = 0;
+
/// Total size of the memory block
/// Type 3 or 'unused' bytes are in [Used,Size-1].
- int Size;
- uint8_t *Ptr() { return (uint8_t*) (this + 1); }
+ int Size = 0;
- Block()
+ uint8_t *Ptr()
+ {
+ return (uint8_t*) (this + 1);
+ }
+
+ uint8_t *Start()
{
- Next = 0;
- Size = 0;
- Used = 0;
+ return Ptr() + Next;
}
+
+ ssize_t Length()
+ {
+ return Used - Next;
+ }
+
+ void Free();
};
ssize_t PreAlloc;
List Mem;
+ bool _debug = false;
public:
/// Constructor
LMemQueue
(
/// Sets the block size, which means allocating ahead and then joining
/// together smaller inserts into 1 continuous block.
size_t PreAlloc = 0
);
/// Destructor
virtual ~LMemQueue();
LMemQueue &operator =(LMemQueue &p);
/// Empties the container freeing any memory used.
virtual void Empty();
/// Returns a dynamically allocated block that contains all the data in the container
/// in a continuous block.
virtual void *New
(
/// If this is > 0 then the function add's the specified number of bytes containing
/// the value 0 on the end of the block. This is useful for NULL terminating strings
/// or adding buffer space on the end of the block returned.
- int AddBytes = 0
+ ssize_t AddBytes = 0
);
/// Reads data from the start of the container without removing it from
/// the que. Returns the bytes copied.
virtual int64 Peek
(
/// Buffer for output
uchar *Ptr,
/// Bytes to look at
- int Size
+ ssize_t Size
);
/// Gets the total bytes in the container
int64 GetSize() override;
/// Reads bytes off the start of the container
ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) override;
/// Writes bytes to the end of the container
ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override;
bool Write(const LString &s) { return Write(s.Get(), s.Length()) == s.Length(); }
/// Search for a substring and return it's index, or -1 if not found.
ssize_t Find(LString str, bool caseSensitive = true);
/// Iterate over the data in the container
/// Callback should return true to keep iterating...
void Iterate(std::function callback, bool reverse = false);
/// For processes that read from one source and then write into this container, it is better
/// to not have to copy the data into some local buffer and then again into the mem queue.
/// So this allows allocating internal blocks and returning them directly to the writer. After
/// the source has written into the internal block the client calls Buffer::Commit to tell the
/// LMemQueue how much data has been written.
class LgiClass Buffer
{
friend class LMemQueue;
LMemQueue *mq = NULL;
Block *blk = NULL;
public:
uint8_t *ptr = NULL;
size_t len = 0;
Buffer(LMemQueue *memq) : mq(memq) {}
// Call this after writing data into the 'ptr' buffer.
bool Commit(size_t bytes);
};
/// Find a buffer and return it. Call Buffer::Commit after writting data to the start of the block.
/// On error Buffer.ptr is NULL.
Buffer GetBuffer();
};
/// A version of GBytePipe for strings. Adds some special handling for strings.
class LgiClass LStringPipe : public LMemQueue
{
ssize_t LineChars();
ssize_t SaveToBuffer(char *Str, ssize_t BufSize, ssize_t Chars);
public:
/// Constructs the object
LStringPipe
(
/// Number of bytes to allocate per block.
int PreAlloc = -1
) : LMemQueue(PreAlloc) {}
~LStringPipe() {}
virtual ssize_t Pop(char *Str, ssize_t Chars);
virtual ssize_t Pop(LArray &Buf);
virtual LString Pop();
virtual ssize_t Push(const char *Str, ssize_t Chars = -1);
virtual ssize_t Push(const char16 *Str, ssize_t Chars = -1);
char *NewStr() { return (char*)New(sizeof(char)); }
LString NewGStr();
char16 *NewStrW() { return (char16*)New(sizeof(char16)); }
LStringPipe &operator +=(const LString &s) { Write(s.Get(), s.Length()); return *this; }
#ifdef _DEBUG
static bool UnitTest();
#endif
};
-#define GMEMFILE_BLOCKS 8
-class LgiClass GMemFile : public LStream
+#define LMEMFILE_BLOCKS 8
+class LgiClass LMemFile : public LStream
{
struct Block
{
size_t Offset;
size_t Used;
uint8_t Data[1];
};
// The current file pointer
- uint64 CurPos;
-
+ uint64 CurPos;
/// Number of blocks in use
int Blocks;
/// Data payload of blocks
int BlockSize;
/// Some blocks are built into the struct, no memory alloc needed.
- Block *Local[GMEMFILE_BLOCKS];
+ Block *Local[LMEMFILE_BLOCKS];
// Buf if they run out we can alloc some more.
LArray Extra;
Block *Get(int Index);
bool FreeBlock(Block *b);
Block *GetLast() { return Get(Blocks-1); }
Block *Create();
int CurBlock() { return (int) (CurPos / BlockSize); }
public:
- GMemFile(int BlkSize = 256);
- ~GMemFile();
+ LMemFile(int BlkSize = 256);
+ ~LMemFile();
void Empty();
int64 GetSize() override;
int64 SetSize(int64 Size) override;
int64 GetPos() override;
int64 SetPos(int64 Pos) override;
ssize_t Read(void *Ptr, ssize_t Size, int Flags = 0) override;
ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) override;
};
#endif
diff --git a/include/lgi/common/Range.h b/include/lgi/common/Range.h
--- a/include/lgi/common/Range.h
+++ b/include/lgi/common/Range.h
@@ -1,105 +1,119 @@
#ifndef _GRANGE_H_
#define _GRANGE_H_
#include
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? a : b)
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? a : b)
#endif
struct LRange
{
ssize_t Start;
ssize_t Len;
// No arguments
LRange()
{
Start = 0;
Len = 0;
}
// One argument = length
LRange(ssize_t len)
{
Start = 0;
Len = len;
}
// Both arguments
LRange(ssize_t start, ssize_t len)
{
Start = start;
Len = len;
}
LRange &Set(ssize_t s, ssize_t l)
{
Start = s;
Len = l;
return *this;
}
LRange Overlap(const LRange &r)
{
LRange o;
if (r.Start >= End())
return o;
if (r.End() <= Start)
return o;
ssize_t e = MIN(End(), r.End());
o.Start = MAX(r.Start, Start);
o.Len = e - o.Start;
return o;
}
bool Overlap(ssize_t Val)
{
return (Val == Start) || (Val >= Start && Val < End());
}
ssize_t End() const
{
return Start + Len;
}
bool Valid() const
{
return Start >= 0 && Len > 0;
}
bool operator ==(const LRange &r) const { return Start == r.Start && Len == r.Len; }
bool operator !=(const LRange &r) const { return Start != r.Start || Len != r.Len; }
bool operator >(const LRange &r) const { return Start > r.Start; }
bool operator >=(const LRange &r) const { return Start >= r.Start; }
bool operator <(const LRange &r) const { return Start < r.Start; }
bool operator <=(const LRange &r) const { return Start < r.Start; }
LRange &operator -=(const LRange &del)
{
LRange o = Overlap(del);
if (o.Valid())
{
assert(o.Len <= Len);
Len -= o.Len;
if (del.Start < o.Start)
Start = del.Start;
}
// else nothing happens
return *this;
}
LRange &operator =(const LRange &r)
{
this->Start = r.Start;
this->Len = r.Len;
return *this;
}
+
+ #ifdef LPrintfSSizeT
+ const char *GetStr()
+ {
+ static const int strs = 4;
+ static char s[strs][32];
+ static int idx = 0;
+
+ char *buf = s[idx++];
+ sprintf_s(buf, 32, LPrintfSSizeT "," LPrintfSSizeT, Start, Len);
+ if (idx >= strs) idx = 0;
+ return buf;
+ }
+ #endif
};
#endif
\ No newline at end of file
diff --git a/include/lgi/win/LgiOsDefs.h b/include/lgi/win/LgiOsDefs.h
--- a/include/lgi/win/LgiOsDefs.h
+++ b/include/lgi/win/LgiOsDefs.h
@@ -1,309 +1,307 @@
//
// FILE: LgiOsDefs.h (Win32)
// AUTHOR: Matthew Allen
// DATE: 24/9/99
// DESCRIPTION: Lgi Win32 OS defines
//
// Copyright (C) 1999, Matthew Allen
// fret@memecode.com
//
-#ifndef __LGI_OS_DEFS_H
-#define __LGI_OS_DEFS_H
+#pragma once
#pragma warning(disable : 4251 4275)
#define WIN32GTK 0
#define WINNATIVE 1
#define LGI_VIEW_HANDLE 1
#define LGI_VIEW_HASH 0
#ifndef WINDOWS
#error "Define WINDOWS in your project"
#endif
#ifdef _WIN64
#define LGI_64BIT 1
#ifndef WIN64
#define WIN64 1
#endif
#else
#define LGI_32BIT 1
#ifndef WIN32
#define WIN32 1
#endif
#endif
#include
#include
#include "lgi/common/LgiInc.h"
#include "lgi/common/LgiDefs.h"
#define _MSC_VER_VS2019 1920 // MSVC++ 16.0 (really all 192x values)
#define _MSC_VER_VS2017 1910 // MSVC++ 15.0
#define _MSC_VER_VS2015 1900 // MSVC++ 14.0
#define _MSC_VER_VS2013 1800 // MSVC++ 12.0
#define _MSC_VER_VS2012 1700 // MSVC++ 11.0
#define _MSC_VER_VS2010 1600 // MSVC++ 10.0
#define _MSC_VER_VS2008 1500 // MSVC++ 9.0
#define _MSC_VER_VS2005 1400 // MSVC++ 8.0
#define _MSC_VER_VS2003 1310 // MSVC++ 7.1
#define _MSC_VER_VC7 1300 // MSVC++ 7.0
#define _MSC_VER_VC6 1200 // MSVC++ 6.0
#define _MSC_VER_VC5 1100 // MSVC++ 5.0
#if _MSC_VER >= _MSC_VER_VS2019
#define _MSC_VER_STR "16"
#define _MSC_YEAR_STR "19"
#elif _MSC_VER >= _MSC_VER_VS2017
#define _MSC_VER_STR "15"
#define _MSC_YEAR_STR "17"
#elif _MSC_VER >= _MSC_VER_VS2015
#define _MSC_VER_STR "14"
#define _MSC_YEAR_STR "15"
#elif _MSC_VER >= _MSC_VER_VS2013
#define _MSC_VER_STR "12"
#define _MSC_YEAR_STR "13"
#elif _MSC_VER >= _MSC_VER_VS2012
#define _MSC_VER_STR "11"
#define _MSC_YEAR_STR "12"
#elif _MSC_VER >= _MSC_VER_VS2010
#define _MSC_VER_STR "10"
#define _MSC_YEAR_STR "10"
#else
#define _MSC_VER_STR "9"
#endif
//////////////////////////////////////////////////////////////////
// Includes
#define WIN32_LEAN_AND_MEAN
#include "winsock2.h"
#include "windows.h"
#include "SHELLAPI.H"
#include "COMMDLG.H"
#include
//////////////////////////////////////////////////////////////////
// Typedefs
typedef HWND OsWindow;
typedef HWND OsView;
typedef HANDLE OsThread;
typedef HANDLE OsProcess;
typedef char16 OsChar;
typedef HBITMAP OsBitmap;
typedef HDC OsPainter;
typedef HFONT OsFont;
#if _MSC_VER <= _MSC_VER_VC6
typedef unsigned long ULONG_PTR, *PULONG_PTR;
#define sprintf_s _snprintf
#endif
typedef BOOL (__stdcall *pSHGetSpecialFolderPathA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
typedef BOOL (__stdcall *pSHGetSpecialFolderPathW)(HWND hwndOwner, LPWSTR lpszPath, int nFolder, BOOL fCreate);
typedef int (__stdcall *pSHFileOperationA)(LPSHFILEOPSTRUCTA lpFileOp);
typedef int (__stdcall *pSHFileOperationW)(LPSHFILEOPSTRUCTW lpFileOp);
typedef int (__stdcall *p_vscprintf)(const char *format, va_list argptr);
#include "lgi/common/Message.h"
-#include "lgi/common/LgiUiBase.h"
class LgiClass OsAppArguments
{
LAutoWString CmdLine;
void _Default();
public:
HINSTANCE hInstance;
DWORD Pid;
char16 *lpCmdLine;
int nCmdShow;
OsAppArguments();
OsAppArguments(int Args, const char **Arg);
OsAppArguments &operator =(OsAppArguments &p);
void Set(char *Utf);
void Set(int Args, const char **Arg);
};
//////////////////////////////////////////////////////////////////
// Defines
#define IsWin9x (LApp::Win9x)
#define DefaultOsView(t) NULL
#define GWL_LGI_MAGIC 8
#define GWL_EXTRA_BYTES 12
// Key redefs
enum LgiKeys {
LK_TAB = VK_TAB,
LK_RETURN = VK_RETURN,
LK_SPACE = VK_SPACE,
LK_DELETE = VK_DELETE,
LK_ESCAPE = VK_ESCAPE,
LK_PAGEUP = VK_PRIOR,
LK_PAGEDOWN = VK_NEXT,
LK_BACKSPACE = VK_BACK,
LK_F1 = VK_F1,
LK_F2 = VK_F2,
LK_F3 = VK_F3,
LK_F4 = VK_F4,
LK_F5 = VK_F5,
LK_F6 = VK_F6,
LK_F7 = VK_F7,
LK_F8 = VK_F8,
LK_F9 = VK_F9,
LK_F10 = VK_F10,
LK_F11 = VK_F11,
LK_F12 = VK_F12,
LK_LEFT = VK_LEFT,
LK_RIGHT = VK_RIGHT,
LK_UP = VK_UP,
LK_DOWN = VK_DOWN,
LK_HOME = VK_HOME,
LK_END = VK_END,
LK_INSERT = VK_INSERT,
LK_SHIFT = VK_SHIFT,
LK_ALT = VK_MENU,
LK_CTRL = VK_CONTROL,
LK_DECIMAL = VK_DECIMAL,
LK_COMMA = VK_OEM_COMMA, // , .
LK_MINUS = VK_OEM_MINUS, // - _
LK_EQUALS = VK_OEM_PLUS, // = +
LK_SEMI_COLON = VK_OEM_1, // ; :
LK_SLASH = VK_OEM_2, // / ?
LK_TILDE = VK_OEM_3, // ~
LK_OPEN_BRACKET = VK_OEM_4, // [ {
LK_BACK_SLASH = VK_OEM_5, // \ |
LK_CLOSE_BRACKET = VK_OEM_6, // ] }
LK_SINGLE_QUOTE = VK_OEM_7, // ' "
};
// Sleep the current thread
LgiFunc void LSleep(DWORD i);
// Process
typedef DWORD OsProcessId;
LgiExtern HINSTANCE _lgi_app_instance;
-#define LProcessInst() _lgi_app_instance
+#define LProcessInst() _lgi_app_instance
extern p_vscprintf lgi_vscprintf;
#define LGetCurrentProcess() GetCurrentProcessId()
// Threads
typedef DWORD OsThreadId;
typedef CRITICAL_SECTION OsSemaphore;
-#define LGetCurrentThread() GetCurrentThread()
+#define LGetCurrentThread() GetCurrentThread()
// #define GetCurrentThreadId() GetCurrentThreadId()
// Socket/Network
#define ValidSocket(s) ((s) != INVALID_SOCKET)
typedef SOCKET OsSocket;
#define LGI_GViewMagic 0x14412662
#define LGI_FileDropFormat "CF_HDROP"
#define LGI_StreamDropFormat CFSTR_FILEDESCRIPTORW
#define LGI_WideCharset "ucs-2"
#define LPrintfInt64 "%I64i"
#define LPrintfHex64 "%I64x"
+
#if LGI_64BIT
#define LPrintfSizeT "%I64u"
#define LPrintfSSizeT "%I64d"
#else
#define LPrintfSizeT "%u"
#define LPrintfSSizeT "%d"
#endif
#define LGI_IllegalFileNameChars "\t\r\n/\\:*?\"<>|"
#define MK_LEFT MK_LBUTTON
#define MK_RIGHT MK_RBUTTON
#define MK_MIDDLE MK_MBUTTON
#define MK_CTRL MK_CONTROL
// Stupid mouse wheel defines don't work. hmmm
#define WM_MOUSEWHEEL 0x020A
#define WHEEL_DELTA 120
#ifndef SPI_GETWHEELSCROLLLINES
#define SPI_GETWHEELSCROLLLINES 104
#endif
// Window flags
#define GWF_VISIBLE WS_VISIBLE
#define GWF_DISABLED WS_DISABLED
#define GWF_BORDER WS_BORDER
#define GWF_TABSTOP WS_TABSTOP
#define GWF_FOCUS 0x00000001
#define GWF_OVER 0x00000002
#define GWF_DROP_TARGET 0x00000004
#define GWF_SUNKEN 0x00000008
#define GWF_FLAT 0x00000010
#define GWF_RAISED 0x00000020
#define GWF_DIALOG 0x00000040
#define GWF_DESTRUCTOR 0x00000080
#define GWF_QUIT_WND 0x00000100
#define GWF_SYS_BORDER 0x00000200 // ask the system to draw the border
// Widgets
#define DialogToPixelX(i) (((i)*Bx)/4)
#define DialogToPixelY(i) (((i)*By)/8)
#define PixelToDialogX(i) (((i)*4)/Bx)
#define PixelToDialogY(i) (((i)*8)/By)
#define DIALOG_X 1.56
#define DIALOG_Y 1.85
#define CTRL_X 1.50
#define CTRL_Y 1.64
// Directories
#define DIR_CHAR '\\'
#define DIR_STR "\\"
#define EOL_SEQUENCE "\r\n"
#define LGI_PATH_SEPARATOR ";"
#define LGI_ALL_FILES "*.*"
#define LGI_LIBRARY_EXT "dll"
#define LGI_EXECUTABLE_EXT ".exe"
/////////////////////////////////////////////////////////////////////////////////////
// Typedefs
typedef HWND OsView;
/////////////////////////////////////////////////////////////////////////////////////
// Externs
LgiFunc class LViewI *LWindowFromHandle(OsView hWnd);
LgiFunc int GetMouseWheelLines();
LgiFunc int WinPointToHeight(int Pt, HDC hDC = NULL);
LgiFunc int WinHeightToPoint(int Ht, HDC hDC = NULL);
LgiExtern class LString WinGetSpecialFolderPath(int Id);
/// Convert a string d'n'd format to an OS dependant integer.
LgiFunc int FormatToInt(LString s);
/// Convert a Os dependant integer d'n'd format to a string.
LgiFunc char *FormatToStr(int f);
extern bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor);
#ifdef _MSC_VER
#define snprintf _snprintf
//#define vsnprintf _vsnprintf
#define vsnwprintf _vsnwprintf
#define stricmp _stricmp
#define strlwr _strlwr
#define strnicmp _strnicmp
#define vsnprintf _vsnprintf_s
// #define getcwd _getcwd
#endif
#define atoi64 _atoi64
#ifdef __GNUC__
// #define stricmp strcasecmp
// #define strnicmp strncasecmp
#define vsnprintf_s vsnprintf
#define swprintf_s snwprintf
#define vsprintf_s vsnprintf
#if !defined(_TRUNCATE)
#define _TRUNCATE ((size_t)-1)
#endif
#endif
-#endif
diff --git a/src/common/Gdc2/Font/Charset.cpp b/src/common/Gdc2/Font/Charset.cpp
--- a/src/common/Gdc2/Font/Charset.cpp
+++ b/src/common/Gdc2/Font/Charset.cpp
@@ -1,1715 +1,1711 @@
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Font.h"
#include "lgi/common/Charset.h"
struct UnicodeMappings
{
int Unicode;
char Ascii;
}
MissingMaps[] =
{
{0x2019, '\''},
{0x201C, '\"'},
{0x201D, '\"'},
{0, 0}
};
typedef uint32_t iso2022jp_block[16];
iso2022jp_block *iso2022jp_map[128];
iso2022jp_block iso2022jp_blocks[] =
{
{0,0,0x10000000,0,0,0x53118c,0x800000,0x800000,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0xfffe0000,0xfffe03fb,0x3fb,0},
{0xffff0002,0xffffffff,0x2ffff,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0x33510000,0x80d0063,0,0,0,0,0,0,0x8,0x800,0,0,0xf0000,0,0x140000,0},
{0x6404098d,0x20301f81,0x40000,0xcc3,0xcc,0x20,0,0,0x40000,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0x3999900f,0x99999939,0x804,0,0,0x300c0003,0xc8c0,0x8000},
{0x60,0,0x5,0xa400,0,0,0,0,0,0,0,0,0,0,0,0},
{0x103fffef,0,0xfffffffe,0xffffffff,0x780fffff,0xfffffffe,0xffffffff,0x787fffff,0,0,0,0,0,0,0,0},
{0x43f36f8b,0x9b462442,0xe3e0e82c,0x400a0004,0xdb365f65,0x4497977,0xe3f0ecd7,0x8c56038,0x3403e602,0x35518000,0x7eabe0c8,0x98698200,0x2942a948,0x8060e803,0xad93441c,0x4568c03a},
{0x8656aa60,0x2403f7a,0x14618388,0x21741020,0x7022021,0x40bc3000,0x4462a624,0xa2060a8,0x85740217,0x9c840402,0x14157bfb,0x11e27f24,0x2efb665,0x20ff1f75,0x38403a70,0x676326c3},
{0x20924dd9,0xfc946b0,0x4850bc98,0xa03f8638,0x88162388,0x52323e09,0xe3a422aa,0xc72c00dd,0x26e1a166,0x8f0a840b,0x559e27eb,0x89bbc241,0x85400014,0x8496361,0x8ad07f0c,0x5cfff3e},
{0xa803ff1a,0x7b407a41,0x80024745,0x38eb0500,0x5d851,0x710c9934,0x1000397,0x24046366,0x5180d0,0x430ac000,0x30c89071,0x58000008,0xf7000e99,0x415f80,0x941000b0,0x62800018},
{0x9d00240,0x1568200,0x8015004,0x5101d10,0x1084c1,0x10504025,0x4d8a410f,0xa60d4009,0x914cab19,0x98121c0,0x3c485,0x80000652,0x80b04,0x9041d,0x905c4849,0x16900009},
{0x22200c65,0x24338412,0x47960c03,0x42250a04,0x90880028,0x4f084900,0xd3aa14a2,0x3e87d830,0x1f618604,0x41867ea4,0x5b3c390,0x211857a5,0x2a48241e,0x4a041128,0x161b0a40,0x88400d60},
{0x9502020a,0x10608221,0x4000243,0x80001444,0xc040000,0x70000000,0xc11a06,0xc00024a,0x401a00,0x40451404,0xbdb30029,0x52b0a78,0xbfa0bba9,0x8379407c,0xe81d12fc,0xc5694bf6},
{0x44aeff6,0xff022115,0x402bed63,0x242d033,0x131000,0x59ca1b02,0x20000a0,0x2c41a703,0x8ff24880,0x204,0x10055800,0x489200,0x20011894,0x34805004,0x684c3200,0x68be49ea},
{0x2e42184c,0x21c9a820,0x80b050b9,0xff7c001e,0x14e0849a,0x1e028c1,0xac49870e,0xdddb130f,0x89fbbe1a,0x51a2a2e0,0x32ca5502,0x928b3e46,0x438f1dbf,0x32186703,0x33c03028,0xa9230811},
{0x3a65c000,0x4028fe3,0x86252c4e,0xa1bf3d,0x8cd43a1a,0x317c06c9,0x950a00e0,0xedb018b,0x8c20e34b,0xf0101182,0xa7287d94,0x40fbc9ac,0x6534484,0x44445a90,0x13fc8,0xf5d40048},
{0xec577701,0x891dc442,0x49286b83,0xd2424109,0x59fe061d,0x3a221800,0x3b9fb7e4,0xc0eaf003,0x82021386,0xe4008980,0x10a1b200,0xcc44b80,0x8944d309,0x48341faf,0xc458259,0x450420a},
{0x10c8a040,0x44503140,0x1004004,0x5408280,0x442c0108,0x1a056a30,0x51420a6,0x645690cf,0x31000021,0xcbf09c18,0x63e2a120,0x1b5104c,0x9a83538c,0x3281b8b2,0xa84987a,0xc0233e7},
{0x9018d4cc,0x9070a1a1,0xe0048a1e,0x451c3d4,0x21c2439a,0x53104844,0x36400292,0xf3bd0241,0xe8f0ab09,0xa5d27dc0,0xd24bc242,0xd0afa43f,0x34a11aa0,0x3d88247,0x651bc452,0xc83ad294},
{0x40c8001c,0x33140e06,0xb21b614f,0xc0d00088,0xa898a02a,0x166ba1c5,0x85b42e50,0x604c08b,0x1e04f933,0xa251056e,0x76380400,0x73b8ec07,0x18324406,0xc8164081,0x63097c8a,0xaa042980},
{0xca9c1c24,0x27604e0e,0x83000990,0x81040046,0x10816011,0x908540d,0xcc0a000e,0xc000500,0xa0440430,0x6784008b,0x8a195288,0x8b18865e,0x41602e59,0x9cbe8c10,0x891c6861,0x89800},
{0x89a8100,0x41900018,0xe4a14007,0x640d0505,0xe4d310e,0xff0a4806,0x2aa81632,0xb852e,0xca841800,0x696c0e20,0x16000032,0x3905658,0x1a285120,0x11248000,0x432618e1,0xeaa5d52},
{0xae280fa0,0x4500fa7b,0x89406408,0xc044c880,0xb1419005,0x24c48424,0x603a1a34,0xc1949000,0x3a8246,0xc106180d,0x99100022,0x1511e050,0x824057,0x20a041a,0x8930004f,0x444ad813},
{0xed228a02,0x400510c0,0x1021000,0x31018808,0x2044600,0x708f000,0xa2008900,0x22020000,0x16100200,0x10400042,0x2605200,0x200052f4,0x82308510,0x42021100,0x80b54308,0x9a2070e1},
{0x8012040,0xfc653500,0xab0419c1,0x62140286,0x440087,0x2449085,0xa85405c,0x33803207,0xb8c00400,0xc0d0ce20,0x80c030,0xd250508,0x400a90,0x80c0200,0x40006505,0x41026421},
{0x268,0x847c0024,0xde200002,0x40498619,0x40000808,0x20010084,0x10108400,0x1c742cd,0xd52a7038,0x1d8f1968,0x3e12be50,0x81d92ef5,0x2412cec4,0x732e0828,0x4b3424ac,0xd41d020c},
{0x80002a02,0x8110097,0x114411c4,0x7d451786,0x64949d9,0x87914000,0xd8c4254c,0x491444ba,0xc8001b92,0x15800271,0xc000081,0xc200096a,0x40024800,0xba493021,0x1c802080,0x1008e2ac},
{0x341004,0x841400e1,0x20000020,0x10149800,0x4aa70c2,0x54208688,0x4130c62,0x20109180,0x2064082,0x54001c40,0xe4e90383,0x84802125,0x2000e433,0xe60944c0,0x81260a03,0x80112da},
{0x97906901,0xf8864001,0x81e24d,0xa6510a0e,0x81ec011a,0x8441c600,0xb62cadb8,0x8741a46f,0x4b028d54,0x2681161,0x2057bb60,0x43350a0,0xb7b4a8c0,0x1122402,0x20009ad3,0xc82271},
{0x809e2081,0xe1800c8a,0x8151b009,0x40281031,0x89a52a0e,0x620e69b6,0xd1444425,0x4d548085,0x1fb12c75,0x862dd807,0x4841d87c,0x226e414e,0x9e088200,0xed37f80c,0x75268c80,0x8149313},
{0xc8040e32,0x6ea6484e,0x66702c4a,0xba0126c0,0x185dd30c,0,0,0,0,0x5400000,0x81337020,0x3a54f81,0x641055ec,0x2344c318,0x341462,0x1a090a43},
{0x13a5187b,0xa8480102,0xc5440440,0xe2dd8106,0x2d481af0,0x416b626,0x6e405058,0x31128032,0xc0007e4,0x420a8208,0x803b4840,0x87134860,0x3428850d,0xe5290319,0x870a2345,0x5c1825a9},
{0xd9c577a6,0x3e85e00,0xa7000081,0x41c6cd54,0xa2042800,0x2b0ab860,0xda9e0020,0xe1a08ea,0x11c0427c,0x3768908,0x1058621,0x18a80000,0xc44846a0,0x20220d05,0x91485422,0x28978a01},
{0x87898,0x31221605,0x8804240,0x6a2fa4e,0x92110814,0x9b042002,0x6432e52,0x90105000,0x85ba0041,0x20203042,0x5a04f0b,0x40802708,0x1a930591,0x600df50,0x3021a202,0x4e800630},
{0x4c80cc4,0x8001a004,0xd4316000,0xa020880,0x281c00,0x418e18,0xca106ad0,0x4b00f210,0x1506274d,0x88900220,0x82a85a00,0x81504549,0x80002004,0x2c088804,0x508d1,0x4ac48001},
{0x62e020,0xa42008e,0x6a8c3055,0xe0a5090e,0x42c42906,0x80b34814,0xb330803e,0x731c0102,0x600d1494,0x9400c20,0xc040301a,0xc094a451,0x5c88dca,0xa40c96c2,0x34040001,0x11000c8},
{0xa9c9550d,0x1c5a2428,0x48370142,0x100f7a4d,0x452a32b4,0x9205317b,0x5c44b894,0x458a68d7,0x2ed15097,0x42081943,0x9d40d202,0x20979840,0x64d5409,0,0,0},
{0,0x84800000,0x4215542,0x17001c06,0x61107624,0xb9ddff87,0x5c0a659f,0x3c00245d,0x59adb0,0,0,0x9b28d0,0x2000422,0x44080108,0xac409804,0x90288d0a},
{0xe0018700,0x310400,0x82211794,0x10540019,0x21a2cb2,0x40039c02,0x88043d60,0x7900080c,0xba3c1628,0xcb088640,0x90807274,0x1e,0xd8000000,0x9c87e188,0x4124034,0x2791ae64},
{0xe6fbe86b,0x5366408f,0x537feea6,0xb5e4e32b,0x2869f,0x1228548,0x8004402,0x20a02116,0x2040004,0x52000,0x1547e00,0x1ac162c,0x10852a84,0x5308c14,0xb943fbc3,0x906000ca},
{0x40326000,0x80901200,0x4c810b30,0x40020054,0x1d6a0029,0x2802000,0x48000,0x150c2610,0x7018040,0xc24d94d,0x18502810,0x50205001,0x4d01000,0x2017080,0x21c30108,0x132},
{0x7190088,0x5600802,0x4c0e0012,0xf0a10405,0x2,0,0,0,0,0,0,0x800000,0x35a8e8d,0x5a0421bd,0x11703488,0x26},
{0x10000000,0x8804c502,0xf801b815,0x25ed147c,0x1bb0ed60,0x1bd70589,0x1a627af3,0xac50d0c,0x524ae5d1,0x63050490,0x52440354,0x16122b57,0x1101a872,0x182949,0x10080948,0x886c6000},
{0x58f916e,0x39903012,0x4930f840,0x1b8880,0,0x428500,0x98000058,0x7014ea04,0x611d1628,0x60005113,0xa71a24,0,0x3c00000,0x10187120,0xa9270172,0x89066004},
{0x20cc022,0x40810900,0x8ca0202d,0xe34,0,0x11012100,0xc11a8011,0x892ec4c,0x85000040,0x1806c7ac,0x512e03e,0x108000,0x80ce4008,0x2106d01,0x8568641,0x27011e},
{0x83d3750,0x4e05e032,0x48401c0,0x1400081,0,0,0,0x591aa0,0x882443c8,0xc8001d48,0x72030152,0x4049013,0x4008280,0xd148a10,0x2088056,0x2704a040},
{0x4c000000,0,0,0xa3200000,0xa0ae1902,0xdf002660,0x7b15f010,0x3ad08121,0x284180,0x48001003,0x8014cc00,0xc414cf,0x30202000,0x1,0,0},
{0,0,0,0,0,0,0,0,0xffffdf7a,0xefffffff,0x3fffffff,0,0,0,0,0x2}
};
class LgiIso2022Jp
{
public:
LgiIso2022Jp()
{
int n, o = 0, i = 0;
for (n=0; n<3; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];
o += 13;
for (n=0; n<4; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];;
o += 4;
iso2022jp_map[o++] = &iso2022jp_blocks[i++];;
o += 14;
for (n=0; n<41; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];;
o += 47;
iso2022jp_map[o++] = &iso2022jp_blocks[i++];;
LAssert(o == 128);
}
bool CanEncode(char16 *s, ssize_t l)
{
if (s)
{
if (l < 0) l = StrlenW(s);
for (int i=0; i> 9];
if (!*block)
{
return false;
}
u &= 0x1ff;
if (
(
*block[u >> 5]
&
(1 << (u & 0x1f))
)
== 0
)
{
return false;
}
}
return true;
}
return false;
}
} Iso2022Jp;
/////////////////////////////////////////////////////////////////////////////////////
bool LIsUtf8(const char *s, ssize_t len)
{
#define LenCheck(Need) \
if (len >= 0 && (len - (s - Start)) < Need) \
goto Utf8Error;
#define TrailCheck() \
if (!IsUtf8_Trail(*s)) \
goto Utf8Error; \
s++;
if (!s || *s == 0)
return true;
- /*
- {
- ssize_t Len = len >= 0 ? len : Strlen(s);
- printf("Utf: %p %i\n", s, (int)Len);
- for (int i=0; i= end)
+ ||
+ *s == 0
+ )
+ break;
+ LgiTrace("%02.2x,", (uint8_t)*s++);
}
LgiTrace("\n");
#endif
return false;
}
/////////////////////////////////////////////////////////////////////////////////////
short _gdc_usascii_mapping[128] =
{
// 0x80 - 0x8f
0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,
// 0x90 - 0x9f
0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xa5, 0x20a7, 0x192,
// 0xa0 - 0xaf
0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xb2, 0xb0, 0xbf, 0x2310, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb,
// 0xb0 - 0xbf
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
// 0xc0 - 0xcf
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
// 0xd0 - 0xdf
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
// 0xe0 - 0xef
0x3b1, 0x3b2, 0x393, 0x3a0, 0x3a3, 0x3c3, 0x3bc, 0x3c4, 0x3a6, 0x398, 0x3a9, 0x3b4, 0x221e, 0xd8, 0x3b6, 0x2229,
// 0xf0 - 0xff
0x2261, 0xb1, 0x2265, 0x2264, 0x2320, 0x2321, 0xf7, 0x2248, 0xb0, 0x2022, 0x2219, 0x221a, 0x207f, 178, 0x25a0, 0x25a1
};
// This mapping just NUL's out the characters between 0x80 and 0x9f which aren't defined
// in the ISO spec. The rest of the characters map to themselves.
short _gdc_ISO_8859_identity_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
short _gdc_ISO_8859_2_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xa0, 0x104, 0x2d8, 0x141, 0xa4, 0x13d, 0x15a, 0xa7, 0xa8, 0x160, 0x15e, 0x164, 0x179, 0xad, 0x17d, 0x17b,
0xb0, 0x105, 0x2db, 0x142, 0xb4, 0x13e, 0x15b, 0x2c7, 0xb8, 0x161, 0x15f, 0x165, 0x17a, 0x2dd, 0x17e, 0x17c,
0x154, 0xc1, 0xc2, 0x102, 0xc4, 0x139, 0x106, 0xc7, 0x10c, 0xc9, 0x118, 0xcb, 0x11a, 0xcd, 0xce, 0x10e,
0x110, 0x143, 0x147, 0xd3, 0xd4, 0x150, 0xd6, 0xd7, 0x158, 0x16e, 0xda, 0x170, 0xdc, 0xdd, 0x162, 0xdf,
0x155, 0xe1, 0xe2, 0x103, 0xe4, 0x13a, 0x107, 0xe7, 0x10d, 0xe9, 0x119, 0xeb, 0x11b, 0xed, 0xee, 0x10f,
0x111, 0x144, 0x148, 0xf3, 0xf4, 0x151, 0xf6, 0xf7, 0x159, 0x16f, 0xfa, 0x171, 0xfc, 0xfd, 0x163, 0x2d9
};
short _gdc_ISO_8859_3_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xa0, 0x126, 0x2d8, 0xa3, 0xa4, 0, 0x124, 0xa7, 0xa8, 0x130, 0x15e, 0x11e, 0x134, 0xad, 0, 0x17b,
0xb0, 0x127, 0xb2, 0xb3, 0xb4, 0xb5, 0x125, 0xb7, 0xb8, 0x131, 0x15f, 0x11f, 0x135, 0xbd, 0, 0x17c,
0xc0, 0xc1, 0xc2, 0, 0xc4, 0x10a, 0x108, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0, 0xd1, 0xd2, 0xd3, 0xd4, 0x120, 0xd6, 0xd7, 0x11c, 0xd9, 0xda, 0xdb, 0xdc, 0x16c, 0x15c, 0xdf,
0xe0, 0xe1, 0xe2, 0, 0xe4, 0x10b, 0x109, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0, 0xf1, 0xf2, 0xf3, 0xf4, 0x121, 0xf6, 0xf7, 0x11d, 0xf9, 0xfa, 0xfb, 0xfc, 0x16d, 0x15d, 0x2d9
};
short _gdc_ISO_8859_4_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x0A0, 0x104, 0x138, 0x156, 0x0A4, 0x128, 0x13B, 0x0A7, 0x0A8, 0x160, 0x112, 0x122, 0x166, 0x0AD, 0x17D, 0x0AF,
0x0B0, 0x105, 0x2DB, 0x157, 0x0B4, 0x129, 0x13C, 0x2C7, 0x0B8, 0x161, 0x113, 0x123, 0x167, 0x14A, 0x17E, 0x14B,
0x100, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x12E, 0x10C, 0x0C9, 0x118, 0x0CB, 0x116, 0x0CD, 0x0CE, 0x12A,
0x110, 0x145, 0x14C, 0x136, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x172, 0x0DA, 0x0DB, 0x0DC, 0x168, 0x16A, 0x0DF,
0x101, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x12F, 0x10D, 0x0E9, 0x119, 0x0EB, 0x117, 0x0ED, 0x0EE, 0x12B,
0x111, 0x146, 0x14D, 0x137, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x173, 0x0FA, 0x0FB, 0x0FC, 0x169, 0x16B, 0x2D9
};
short _gdc_ISO_8859_5_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x0A0, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x0AD, 0x40E, 0x40F,
0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F,
0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F,
0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F,
0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F,
0x2116, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x0A7, 0x45E, 0x45F
};
short _gdc_ISO_8859_6_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xA0, 0, 0, 0, 0xA4, 0, 0, 0, 0, 0, 0, 0, 0x60C, 0xAD, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x61B, 0, 0, 0, 0x61F,
0, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627, 0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F,
0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0x637, 0x638, 0x639, 0x63A, 0, 0, 0, 0, 0,
0x640, 0x641, 0x642, 0x643, 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0x64A, 0x64B, 0x64C, 0x64D, 0x64E, 0x64F,
0x650, 0x651, 0x652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
short _gdc_ISO_8859_7_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x0A0, 0x2BD, 0x2BC, 0x0A3, 0, 0, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0, 0x0AB, 0x0AC, 0x0AD, 0, 0x015,
0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x384, 0x385, 0x386, 0x0B7, 0x388, 0x389, 0x38A, 0x0BB, 0x38C, 0x0BD, 0x38E, 0x38F,
0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F,
0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF,
0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF,
0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0
};
short _gdc_ISO_8859_8_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x0A0, 0, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0D7, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x203E,
0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0F7, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2017,
0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7, 0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF,
0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7, 0x5E8, 0x5E9, 0x5EA, 0, 0, 0, 0, 0
};
short _gdc_ISO_8859_9_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF
};
short _gdc_ISO_8859_13_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xA0, 0x201D, 0xA2, 0xA3, 0xA4, 0x201E, 0xA6, 0xA7, 0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6,
0xB0, 0xB1, 0xB2, 0xB3, 0x201C, 0xB5, 0xB6, 0xB7, 0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6,
0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112, 0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B,
0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7, 0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF,
0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113, 0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C,
0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7, 0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2019
};
short _gdc_ISO_8859_15_mapping[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0xA0, 0xA1, 0xA2, 0xA3, 0x20AC, 0xA5, 0x160, 0xA7, 0x161, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0x17D, 0xB5, 0xB6, 0xB7, 0x17E, 0xB9, 0xBA, 0xBB, 0x152, 0x153, 0x178, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
short _gdc_win_874_mapping[128] =
{
0x20AC, 0, 0, 0, 0, 0x2026, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0, 0, 0, 0, 0, 0, 0, 0,
0xA0, 0xE01, 0xE02, 0xE03, 0xE04, 0xE05, 0xE06, 0xE07,
0xE08, 0xE09, 0xE0A, 0xE0B, 0xE0C, 0xE0D, 0xE0E, 0xE0F,
0xE10, 0xE11, 0xE12, 0xE13, 0xE14, 0xE15, 0xE16, 0xE17,
0xE18, 0xE19, 0xE1A, 0xE1B, 0xE1C, 0xE1D, 0xE1E, 0xE1F,
0xE20, 0xE21, 0xE22, 0xE23, 0xE24, 0xE25, 0xE26, 0xE27,
0xE28, 0xE29, 0xE2A, 0xE2B, 0xE2C, 0xE2D, 0xE2E, 0xE2F,
0xE30, 0xE31, 0xE32, 0xE33, 0xE34, 0xE35, 0xE36, 0xE37,
0xE38, 0xE39, 0xE3A, 0, 0, 0, 0, 0xE3F,
0xE40, 0xE41, 0xE42, 0xE43, 0xE44, 0xE45, 0xE46, 0xE47,
0xE48, 0xE49, 0xE4A, 0xE4B, 0xE4C, 0xE4D, 0xE4E, 0xE4F,
0xE50, 0xE51, 0xE52, 0xE53, 0xE54, 0xE55, 0xE56, 0xE57,
0xE58, 0xE59, 0xE5A, 0xE5B, 0, 0, 0, 0,
};
short _gdc_win_1250_mapping[128] =
{
0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
0xA0, 0x02C7, 0x02D8, 0x0141, 0xA4, 0x0104, 0xA6, 0xA7, 0xA8, 0xA9, 0x015E, 0xAB, 0xAC, 0xAD, 0xAE, 0x017B,
0xB0, 0xB1, 0x02DB, 0x0142, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0x0105, 0x015F, 0xBB, 0x013D, 0x02DD, 0x013E, 0x017C,
0x0154, 0xC1, 0xC2, 0x0102, 0xC4, 0x0139, 0x0106, 0xC7, 0x010C, 0xC9, 0x0118, 0xCB, 0x011A, 0xCD, 0xCE, 0x010E,
0x0110, 0x0143, 0x0147, 0xD3, 0xD4, 0x0150, 0xD6, 0xD7, 0x0158, 0x016E, 0xDA, 0x0170, 0xDC, 0xDD, 0x0162, 0xDF,
0x0155, 0xE1, 0xE2, 0x0103, 0xE4, 0x013A, 0x0107, 0xE7, 0x010D, 0xE9, 0x0119, 0xEB, 0x011B, 0xED, 0xEE, 0x010F,
0x0111, 0x0144, 0x0148, 0xF3, 0xF4, 0x0151, 0xF6, 0xF7, 0x0159, 0x016F, 0xFA, 0x0171, 0xFC, 0xFD, 0x0163, 0x02D9
};
short _gdc_win_1251_mapping[128] =
{
0x402, 0x403, 0x201A, 0x453, 0x201E, 0x2026, 0x2020, 0x2021,
0x20AC, 0x2030, 0x409, 0x2039, 0x40A, 0x40C, 0x40B, 0x40F,
0x452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0, 0x2122, 0x459, 0x203A, 0x45A, 0x45C, 0x45B, 0x45F,
0x0A0, 0x40E, 0x45E, 0x408, 0x0A4, 0x490, 0x0A6, 0x0A7,
0x401, 0x0A9, 0x404, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x407,
0x0B0, 0x0B1, 0x406, 0x456, 0x491, 0x0B5, 0x0B6, 0x0B7,
0x451, 0x2116, 0x454, 0x0BB, 0x458, 0x405, 0x455, 0x457,
0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417,
0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F,
0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427,
0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F,
0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437,
0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F,
0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447,
0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F
};
short _gdc_win_1252_mapping[128] =
{
0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
short _gdc_win_1253_mapping[128] =
{
0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021,
0, 0x2030, 0, 0x2039, 0, 0, 0, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0, 0x2122, 0, 0x203A, 0, 0, 0, 0,
0xA0, 0x385, 0x386, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0, 0xAB, 0xAC, 0xAD, 0xAE, 0x2015,
0xB0, 0xB1, 0xB2, 0xB3, 0x384, 0xB5, 0xB6, 0xB7,
0x388, 0x389, 0x38A, 0xBB, 0x38C, 0xBD, 0x38E, 0x38F,
0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397,
0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F,
0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7,
0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF,
0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7,
0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF,
0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7,
0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0
};
short _gdc_win_1254_mapping[128] =
{
0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021,
0x2C6, 0x2030, 0x160, 0x2039, 0x152, 0, 0, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x2DC, 0x2122, 0x161, 0x203A, 0x153, 0, 0, 0x178,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF,
};
short _gdc_win_1255_mapping[128] =
{
0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021,
0x2C6, 0x2030, 0, 0x2039, 0, 0, 0, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x2DC, 0x2122, 0, 0x203A, 0, 0, 0, 0,
0xA0, 0xA1, 0xA2, 0xA3, 0x20AA, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xD7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xB8, 0xB9, 0xF7, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0x5B0, 0x5B1, 0x5B2, 0x5B3, 0x5B4, 0x5B5, 0x5B6, 0x5B7,
0x5B8, 0x5B9, 0, 0x5BB, 0x5BC, 0x5BD, 0x5BE, 0x5BF,
0x5C0, 0x5C1, 0x5C2, 0x5C3, 0x5F0, 0x5F1, 0x5F2, 0x5F3,
0x5F4, 0, 0, 0, 0, 0, 0, 0,
0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7,
0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF,
0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7,
0x5E8, 0x5E9, 0x5EA, 0, 0, 0x200E, 0x200F, 0,
};
short _gdc_win_1256_mapping[128] =
{
0x20AC, 0x67E, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021,
0x2C6, 0x2030, 0x679, 0x2039, 0x152, 0x686, 0x698, 0x688,
0x6AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x6A9, 0x2122, 0x691, 0x203A, 0x153, 0x200C, 0x200D, 0x6BA,
0xA0, 0x60C, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0x6BE, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xB8, 0xB9, 0x61B, 0xBB, 0xBC, 0xBD, 0xBE, 0x61F,
0x6C1, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627,
0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F,
0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0xD7,
0x637, 0x638, 0x639, 0x63A, 0x640, 0x641, 0x642, 0x643,
0xE0, 0x644, 0xE2, 0x645, 0x646, 0x647, 0x648, 0xE7,
0xE8, 0xE9, 0xEA, 0xEB, 0x649, 0x64A, 0xEE, 0xEF,
0x64B, 0x64C, 0x64D, 0x64E, 0xF4, 0x64F, 0x650, 0xF7,
0x651, 0xF9, 0x652, 0xFB, 0xFC, 0x200E, 0x200F, 0x6D2,
};
short _gdc_win_1257_mapping[128] =
{
0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021,
0, 0x2030, 0, 0x2039, 0, 0xA8, 0x2C7, 0xB8,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0, 0x2122, 0, 0x203A, 0, 0xAF, 0x2DB, 0,
0xA0, 0, 0xA2, 0xA3, 0xA4, 0, 0xA6, 0xA7,
0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6,
0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112,
0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B,
0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7,
0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF,
0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113,
0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C,
0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7,
0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2D9,
};
short _gdc_win_1258_mapping[128] =
{
0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021,
0x2C6, 0x2030, 0, 0x2039, 0x152, 0, 0, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x2DC, 0x2122, 0, 0x203A, 0x153, 0, 0, 0x178,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0x102, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xCB, 0x300, 0xCD, 0xCE, 0xCF,
0x110, 0xD1, 0x309, 0xD3, 0xD4, 0x1A0, 0xD6, 0xD7,
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x1AF, 0x303, 0xDF,
0xE0, 0xE1, 0xE2, 0x103, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xEB, 0x301, 0xED, 0xEE, 0xEF,
0x111, 0xF1, 0x323, 0xF3, 0xF4, 0x1A1, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x1B0, 0x20AB, 0xFF,
};
short _gdc_koi8r_mapping[128] =
{
0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7,
0x2550, 0x2551, 0x2552, 0x451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E,
0x255F, 0x2560, 0x2561, 0x401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0xA9,
0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E,
0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A,
0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E,
0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A
};
short _gdc_koi8u_mapping[128] =
{
0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2019, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7,
0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x255D, 0x255E,
0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x256C, 0xA9,
0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E,
0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A,
0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E,
0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A
};
short _gdc_koi8ru_mapping[128] =
{
0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
0x2591, 0x2592, 0x2593, 0x201C, 0x25A0, 0x2219, 0x201D, 0x2014, 0x2116, 0x2122, 0xA0, 0xBB, 0xAE, 0xAB, 0xB7, 0xA4,
0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x45E, 0x255E,
0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x40E, 0xA9,
0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E,
0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A,
0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E,
0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A,
};
#if defined WIN32
#define WinDef(d) d
// Map the windows codepages to their real names.
#define WINDOWS_1250 EASTEUROPE_CHARSET
#define WINDOWS_1252 ANSI_CHARSET
#define WINDOWS_1251 RUSSIAN_CHARSET
#define WINDOWS_1253 GREEK_CHARSET
#define WINDOWS_1254 TURKISH_CHARSET
#define WINDOWS_1255 HEBREW_CHARSET
#define WINDOWS_1256 ARABIC_CHARSET
#define WINDOWS_1257 BALTIC_CHARSET
#define WINDOWS_932 SHIFTJIS_CHARSET
#define WINDOWS_936 GB2313_CHARSET
#define WINDOWS_949 HANGEUL_CHARSET
#define WINDOWS_950 CHINESEBIG5_CHARSET
#else
#define WinDef(d) 0
#endif
LCharset::LCharset(const char *cp, const char *des, short *map, const char *alt)
{
Charset = cp;
Description = des;
UnicodeMap = map;
IconvName = 0;
AlternateNames = alt;
Type = CpNone;
if (cp)
{
if (stricmp(cp, "utf-8") == 0)
{
Type = CpUtf8;
}
else if (stricmp(cp, "utf-16") == 0)
{
Type = CpUtf16;
}
else if (stricmp(cp, "utf-32") == 0)
{
Type = CpUtf32;
}
else if (stricmp(cp, "ucs-2") == 0)
{
Type = CpUtf16;
IconvName = "UCS-2-INTERNAL";
}
else if (UnicodeMap)
{
Type = CpMapped;
}
#ifdef WIN32
else if (strnicmp(cp, "windows-", 8) == 0)
{
Type = CpWindowsDb;
}
#endif
else
{
Type = CpIconv;
}
}
}
bool LCharset::IsUnicode()
{
return (Type == CpUtf8) ||
(Type == CpUtf16) ||
(Type == CpUtf32);
}
const char *LCharset::GetIconvName()
{
return IconvName ? IconvName : Charset;
}
bool LCharset::IsAvailable()
{
if (Type != CpIconv)
return true;
#ifndef LGI_STATIC
LFontSystem *FontSys = LFontSystem::Inst();
if (FontSys)
return FontSys->HasIconv(true);
#endif // LGI_STATIC
return false;
}
static LCharset LgiCharsets[] = {
// Good 'ol ascii
LCharset("us-ascii", "ASCII", _gdc_usascii_mapping, "ascii-us,iso-ir-6,ANSI_X3.4-1986,ISO_646.irv,ASCII,ISO646-US,us,IBM367,cp367,csASCII"),
// Unicode (up here so they get found first)
LCharset("utf-8", "Utf-8"),
LCharset("utf-16", "Utf-16"),
LCharset("utf-32", "Utf-32"),
LCharset("ucs-2", "Ucs-2"),
// ISO (get prefered over windows charsets by being first in this list)
LCharset("iso-8859-1", "ISO 8859-1 (West Europe)", _gdc_ISO_8859_identity_mapping, "iso-ir-100,ISO_8859-1,latin1,l1,IBM819,CP819,csISOLatin1,iso8859-1"),
LCharset("iso-8859-2", "ISO 8859-2 (East Europe)", _gdc_ISO_8859_2_mapping, "iso-ir-101,ISO_8859-2,latin2,l2,csISOLatin2"),
LCharset("iso-8859-3", "ISO 8859-3 (Latin Script)", _gdc_ISO_8859_3_mapping, "iso-ir-109,ISO_8859-3,latin3,l3,csISOLatin3"),
LCharset("iso-8859-4", "ISO 8859-4 (Baltic)", _gdc_ISO_8859_4_mapping, "iso-ir-110,ISO_8859-4,latin4,l4,csISOLatin4"),
LCharset("iso-8859-5", "ISO 8859-5 (Russian)", _gdc_ISO_8859_5_mapping, "iso-ir-144,ISO_8859-5,cyrillic,csISOLatinCyrillic"),
LCharset("iso-8859-6", "ISO 8859-6 (Arabic)", _gdc_ISO_8859_6_mapping, "iso-ir-127,ISO_8859-6,ECMA-114,ASMO-708,arabic,csISOLatinArabic"),
LCharset("iso-8859-7", "ISO 8859-7 (Greek)", _gdc_ISO_8859_7_mapping, "iso-ir-126,ISO_8859-7,ELOT_928,ECMA-118,greek,greek8,csISOLatinGreek"),
LCharset("iso-8859-8", "ISO 8859-8 (Hebrew)", _gdc_ISO_8859_8_mapping, "iso-ir-138,ISO_8859-8,hebrew,csISOLatinHebrew,iso-8859-8-i"),
LCharset("iso-8859-9", "ISO 8859-9 (Turkish)", _gdc_ISO_8859_9_mapping, "iso-ir-148,ISO_8859-9,latin5,l5,csISOLatin5"),
LCharset("iso-8859-13", "ISO 8859-13 (Baltik)", _gdc_ISO_8859_13_mapping, "ISO_8859-9"),
LCharset("iso-8859-15", "ISO 8859-15 (Latic 9)", _gdc_ISO_8859_15_mapping, "ISO_8859-15"),
// Windows
LCharset("windows-874", "Windows 874 (Thai)", _gdc_win_874_mapping, "iso-8859-11,cp874"),
LCharset("windows-932", "Windows 932 (Japanese)"),
LCharset("windows-936", "Windows 936 (Chinese)"),
LCharset("windows-949", "Windows 949 (Korean)"),
LCharset("windows-950", "Windows 950 (Chinese)"),
LCharset("windows-1250", "Windows 1250 (Latin 2)", _gdc_win_1250_mapping, "x-cp1250,cp1250"),
LCharset("windows-1251", "Windows 1251 (Cyrillic)", _gdc_win_1251_mapping, "x-cp1251,cp1251"),
LCharset("windows-1252", "Windows 1252 (Latin 1)", _gdc_win_1252_mapping, "x-cp1252,cp1252"),
LCharset("windows-1253", "Windows 1253 (Greek)", _gdc_win_1253_mapping, "x-cp1253,cp1253"),
LCharset("windows-1254", "Windows 1254 (Turkish)", _gdc_win_1254_mapping, "x-cp1254,cp1254"),
LCharset("windows-1255", "Windows 1255 (Hebrew)", _gdc_win_1255_mapping, "x-cp1255,cp1255"),
LCharset("windows-1256", "Windows 1256 (Arabic)", _gdc_win_1256_mapping, "x-cp1256,cp1256"),
LCharset("windows-1257", "Windows 1257 (Baltic)", _gdc_win_1257_mapping, "x-cp1257,cp1257"),
LCharset("windows-1258", "Windows 1258 (Veitnam)", _gdc_win_1258_mapping, "x-cp1258,cp1258"),
// Russian
LCharset("koi8-r", "KOI8-R", _gdc_koi8r_mapping, "csKOI8R"),
LCharset("koi8-u", "KOI8-U", _gdc_koi8u_mapping, "csKOI8U"),
LCharset("koi8-ru", "KOI8-RU", _gdc_koi8ru_mapping, "csKOI8RU"),
LCharset("koi8-t", "KOI8-T (Tajik)"),
// Codepages
LCharset("cp850", "Cp850", 0, "IBM850,850,csPC850Multilingual"),
LCharset("cp862", "Cp862", 0, "IBM862,862,csPC862LatinHebrew"),
LCharset("cp866", "Cp866", 0, "IBM866,866,csIBM866"),
LCharset("cp1133", "Cp1133 (Laotian)"),
// Japanese
LCharset("euc-jp", "EUC-JP", 0, "csEUCPkdFmtJapanese"),
LCharset("shift_jis", "SHIFT_JIS", 0, "MS_Kanji,csShiftJIS"),
LCharset("cp932", "cp932", 0, 0),
LCharset("iso-2022-jp", "ISO-2022-JP", 0, "csISO2022JP"),
LCharset("iso-2022-jp-1", "ISO-2022-JP-1"),
LCharset("iso-2022-jp-2", "ISO-2022-JP-2", 0, "csISO2022JP2"),
// Chinese
LCharset("euc-cn", "EUC-CN (Chinese)"),
LCharset("hz-gb-2312", "HZ (Chinese)", 0, "hz"),
LCharset("gbk", "GBK (Chinese)", 0, "CP936,MS936,windows-936,x-gbk,gb2312,GB-2312,csGB2312,GB2312_CHARSET"),
LCharset("gb18030", "GB18030 (Chinese)"),
LCharset("euc-tw", "EUC-TW (Chinese)"),
LCharset("big5", "BIG5 (Chinese)", 0, "csBig5"),
LCharset("big5-hkscs", "BIG5-HKSCS (Chinese)"),
// LCharset("gb2312", "GB-2312 (Chinese)", 0, "GB-2312,csGB2312"),
LCharset("iso-2022-cn", "ISO-2022-CN (Chinese)"),
LCharset("iso-2022-cn-eXT","ISO-2022-CN-EXT (Chinese)"),
// Korean
LCharset("euc-kr", "EUC-KR", 0, "csEUCKR"),
LCharset("iso-2022-kr", "ISO-2022-KR", 0, "csISO2022KR"),
LCharset("johab", "JOHAB"),
LCharset("cp949", "CP949", 0, "ks_c_5601-1987,ks_c_5601"),
// Armenian
// LCharset("armscii-8", "ARMSCII-8 (Armenian)"),
// Georgian
LCharset("Georgian-Academy","Georgian-Academy"),
LCharset("Georgian-PS", " Georgian-PS"),
// Thai
LCharset("tis-620", "TIS-620 (Thai)"),
// Laotian
LCharset("mulelao-1", "MuleLao-1"),
// Vietnamese
LCharset("viscii", "VISCII (Vietnamese)", 0, "csVISCII"),
LCharset("tcvn", "TCVN (Vietnamese)"),
// EOF marker
LCharset()
};
static LCharsetSystem CharsetSystem;
LCharsetSystem *LCharsetSystem::Inst()
{
return &CharsetSystem;
}
LCharset *LGetCpInfo(const char *Cs)
{
return CharsetSystem.GetCsInfo(Cs);
}
/////////////////////////////////////////////////////////////////////////////
// Utf-16 conversion
int LCpToAnsi(char *cp)
{
int Ansi = 0;
if (cp &&
strnicmp(cp, "windows-", 8) == 0)
{
Ansi = atoi(cp+9);
}
return Ansi;
}
ssize_t LBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen)
{
int Status = 0;
if (Out && OutCp && In && InCp)
{
LCharset *InInfo = LGetCpInfo(InCp);
LCharset *OutInfo = LGetCpInfo(OutCp);
if (InInfo && OutInfo)
{
char *In8 = (char*)In;
uchar *Out8 = (uchar*)Out;
if (InLen < 0)
{
switch (InInfo->Type)
{
case CpMapped:
case CpUtf8:
case CpIconv:
InLen = (int)strlen((char*)In);
break;
case CpUtf16:
case CpWindowsDb:
InLen = StringLen((uint16*)In) << 1;
break;
case CpUtf32:
InLen = StringLen((uint32_t*)In) << 2;
break;
default:
LAssert(0);
return 0;
}
}
#ifdef WIN32
if (InInfo->Type == CpWindowsDb && OutInfo->Type == CpUtf16)
{
// mb -> unicode
char Cp[32];
sprintf_s(Cp, sizeof(Cp), ".%s", InInfo->Charset + 8);
setlocale(LC_ALL, Cp);
void *Start = Out;
while (OutLen >= sizeof(char16) &&
InLen > 0)
{
int s = mbtowc((char16*)Out, (char*)In, min(InLen, MB_CUR_MAX));
if (s > 0)
{
((char*&)In) += s;
InLen -= s;
((char16*&)Out)++;
OutLen -= sizeof(char16);
}
else break;
}
return (NativeInt)Out-(NativeInt)Start;
}
else if (InInfo->Type == CpUtf16 && OutInfo->Type == CpWindowsDb)
{
// unicode -> mb
char Cp[32];
sprintf_s(Cp, sizeof(Cp), ".%s", OutInfo->Charset + 8);
setlocale(LC_ALL, Cp);
void *Start = Out;
while (OutLen >= MB_CUR_MAX &&
InLen > sizeof(char16) )
{
#if 1
int s = 0;
errno_t err = wctomb_s(&s, (char*)Out, OutLen, ((char16*)In)[0]);
if (err || s == 0)
break;
#else
int s = wctomb((char*)Out, ((char16*)In)[0] );
if (s > 0)
#endif
{
((char16*&)In)++;
InLen -= sizeof(char16);
((char*&)Out) += s;
OutLen -= s;
}
}
return (NativeInt)Out-(NativeInt)Start;
}
else
#endif
if (InInfo->Type == CpIconv ||
OutInfo->Type == CpIconv)
{
#ifndef LGI_STATIC
LFontSystem *Fs = LFontSystem::Inst();
if (Fs)
return Fs->IconvConvert(OutInfo->GetIconvName(), (char*)Out, OutLen, InInfo->GetIconvName(), (const char*&)In, InLen);
#else
LAssert(!"No iconv in static build");
#endif
}
else
{
// Mapped or Utf conversion
uint32_t Utf32 = 0;
while (OutLen > 0 && InLen > 0)
{
char *RewindIn = In8;
ptrdiff_t RewindInLen = InLen;
// Convert input char to Utf-32
switch (InInfo->Type)
{
case CpMapped:
{
if (*In8)
{
uchar i = (uchar)*In8++;
InLen--;
if (i & 0x80)
{
Utf32 = InInfo->UnicodeMap[i - 0x80];
if (!Utf32) Utf32 = '?';
}
else
{
Utf32 = i;
}
}
else
{
Utf32 = 0;
InLen = 0;
}
break;
}
case CpUtf8:
{
Utf32 = LgiUtf8To32((uint8_t *&)In8, InLen);
break;
}
case CpUtf16:
{
Utf32 = LgiUtf16To32((const uint16_t *&)In8, InLen);
if (Utf32 == 0xfeff || Utf32 == 0xfffe)
continue;
break;
}
case CpUtf32:
{
Utf32 = *((uint32_t*&)In8)++;
InLen -= 4;
break;
}
default:
LAssert(0);
break;
}
if (!Utf32)
{
break;
}
// Convert Utf-32 into output format
switch (OutInfo->Type)
{
case CpMapped:
{
if (Utf32 & ~0x7f)
{
int n;
for (n=0; n<128; n++)
{
if (OutInfo->UnicodeMap[n] == Utf32)
{
*Out8++ = 0x80 + n;
break;
}
}
if (n >= 128)
{
for (n=0; MissingMaps[n].Unicode; n++)
{
if (MissingMaps[n].Unicode == Utf32)
{
*Out8++ = MissingMaps[n].Ascii;
break;
}
}
if (!MissingMaps[n].Unicode)
{
*Out8++ = '?';
}
}
}
else
{
*Out8++ = Utf32;
}
OutLen--;
break;
}
case CpUtf8:
{
// uchar *PrevOut8 = Out8;
if (!LgiUtf32To8(Utf32, (uint8_t*&) Out8, OutLen))
{
// Not enough buffer to encode the character
In8 = RewindIn;
InLen = RewindInLen;
OutLen = 0;
}
break;
}
case CpUtf16:
{
LgiUtf32To16(Utf32, (uint16_t*&)Out8, OutLen);
break;
}
case CpUtf32:
{
*((uint32_t*&)Out8)++ = Utf32;
OutLen -= 4;
break;
}
default:
{
break;
}
}
}
In = (void*)In8;
Status = (int) (Out8 - (uchar*)Out);
}
}
else
{
// printf("%s:%i - LBufConvertCp failed '%s' -> '%s'.\n", __FILE__, __LINE__, InCp, OutCp);
}
}
return Status;
}
template
T *DupeString(T *s, ssize_t Len = -1)
{
if (!s)
return NULL;
if (Len < 0)
{
Len = 0;
while (s[Len])
Len++;
}
T *ns = new T[Len+1];
if (!ns)
return NULL;
memcpy(ns, s, sizeof(T) * Len);
ns[Len] = 0;
return ns;
}
LString LStrConvertCp(const char *OutCp, const void *In, const char *InCp, ssize_t InLen)
{
if (!OutCp || !In || !InCp)
return LString();
LCharset *InInfo = LGetCpInfo(InCp);
LCharset *OutInfo = LGetCpInfo(OutCp);
if (!InInfo || !OutInfo)
return LString();
if (InLen < 0)
{
switch (InInfo->Type)
{
case CpMapped:
case CpUtf8:
case CpIconv:
InLen = (int)strlen((char*)In);
break;
case CpUtf16:
case CpWindowsDb:
InLen = StringLen((uint16*)In) << 1;
break;
case CpUtf32:
InLen = StringLen((uint32_t*)In) << 2;
break;
default:
return LString();
}
}
switch (OutInfo->Type)
{
case CpMapped:
case CpUtf8:
case CpIconv:
break;
case CpUtf16:
case CpWindowsDb:
case CpUtf32:
default:
LAssert(!"LString doesn't >8bit char (yet).");
return LString();
}
if (!stricmp(InCp, OutCp))
return LString((char*)In, InLen);
LStringPipe b;
if (InInfo->Type == CpIconv ||
OutInfo->Type == CpIconv)
{
#ifndef LGI_STATIC
LFontSystem *Fs = LFontSystem::Inst();
if (Fs)
{
auto InCs = InInfo->GetIconvName();
auto OutCs = OutInfo->GetIconvName();
if (Fs->IconvConvert(OutCs, &b, InCs, (const char*&)In, InLen))
return b.NewGStr();
InCp = "iso-8859-1";
}
#else
LAssert(!"No inconv in static build");
#endif
}
char Buf[2 << 10];
while (InLen > 0)
{
ssize_t Bytes = LBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen);
if (Bytes > 0)
b.Write((uchar*)Buf, (int)Bytes);
else
break;
}
return b.NewGStr();
}
void *LNewConvertCp(const char *OutCp, const void *In, const char *InCp, ssize_t InLen)
{
if (!OutCp || !In || !InCp)
return NULL;
LCharset *InInfo = LGetCpInfo(InCp);
LCharset *OutInfo = LGetCpInfo(OutCp);
if (!InInfo || !OutInfo)
return NULL;
LMemQueue b;
if (InLen < 0)
{
switch (InInfo->Type)
{
case CpMapped:
case CpUtf8:
case CpIconv:
InLen = (int)strlen((char*)In);
break;
case CpUtf16:
case CpWindowsDb:
InLen = StringLen((uint16*)In) << 1;
break;
case CpUtf32:
InLen = StringLen((uint32_t*)In) << 2;
break;
default:
LAssert(0);
return NULL;
}
}
int NullSize;
switch (OutInfo->Type)
{
case CpMapped:
case CpUtf8:
case CpIconv:
NullSize = 1;
break;
case CpUtf16:
case CpWindowsDb:
NullSize = 2;
break;
case CpUtf32:
NullSize = 4;
break;
default:
LAssert(0);
return NULL;
}
if (!stricmp(InCp, OutCp))
{
if (InInfo->Type == CpUtf16)
return DupeString((uint16*)In, InLen/sizeof(uint16));
else if (InInfo->Type == CpUtf32)
return DupeString((uint32_t*)In, InLen/sizeof(uint32_t));
else
return NewStr((char*)In, InLen);
}
if (InInfo->Type == CpIconv ||
OutInfo->Type == CpIconv)
{
#ifndef LGI_STATIC
LFontSystem *Fs = LFontSystem::Inst();
if (Fs)
{
const char *InCs = InInfo->GetIconvName();
const char *OutCs = OutInfo->GetIconvName();
if (!Fs->IconvConvert(OutCs, &b, InCs, (const char*&)In, InLen))
{
InCp = "iso-8859-1";
goto BufConvert;
}
}
#else
LAssert(!"No inconv in static build");
#endif
}
else
{
BufConvert:
char Buf[2 << 10];
while (InLen > 0)
{
ssize_t Bytes = LBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen);
if (Bytes > 0)
{
b.Write((uchar*)Buf, (int)Bytes);
}
else
{
break;
}
}
}
return b.GetSize() ? b.New(NullSize) : 0;
}
int LCharLen(const void *Str, const char *Cp, int Bytes)
{
if (Str && Cp)
{
LCharset *InInfo = LGetCpInfo(Cp);
if (InInfo)
{
switch (InInfo->Type)
{
default:
case CpMapped:
{
return (int)strlen((char*)Str);
}
case CpUtf8:
{
uchar *s = (uchar*)Str;
int Len = 0;
if (Bytes > 0)
{
uchar *e = s + Bytes;
while (*s && s < e)
{
LgiNextUtf8((char*&)s);
Len++;
}
}
else
{
while (*s)
{
LgiNextUtf8((char*&)s);
Len++;
}
}
return Len;
}
case CpUtf16:
{
return StringLen((uint16*)Str);
}
case CpUtf32:
{
return StringLen((uint32_t*)Str);
}
}
}
}
return 0;
}
bool LIsCpImplemented(char *Cp)
{
return LGetCpInfo(Cp) != 0;
}
const char *LAnsiToLgiCp(int AnsiCodePage)
{
if (AnsiCodePage < 0)
{
#ifdef WIN32
AnsiCodePage = GetACP();
#else
return "utf-8";
#endif
}
#define WinCp(i) case i: return "windows-" #i;
switch (AnsiCodePage)
{
WinCp(874)
WinCp(932)
WinCp(936)
WinCp(949)
WinCp(950)
WinCp(1250)
WinCp(1251)
WinCp(1252)
WinCp(1253)
WinCp(1254)
WinCp(1255)
WinCp(1256)
WinCp(1257)
WinCp(1258)
case 20127:
return "us-ascii";
case 28591:
return "iso-8859-1";
case 28592:
return "iso-8859-2";
case 28593:
return "iso-8859-3";
case 28594:
return "iso-8859-4";
case 28595:
return "iso-8859-5";
case 28596:
return "iso-8859-6";
case 28597:
return "iso-8859-7";
case 28598:
return "iso-8859-8";
case 28599:
return "iso-8859-9";
case 28600:
return "ISO-8859-10";
case 28605:
return "ISO-8859-15";
case 50220:
case 50221:
return "iso-2022-jp";
case 51932:
return "euc-jp";
case 51949:
return "euc-kr";
case 65001:
return "utf-8";
}
#undef WinCp
return 0;
}
char *LSeekUtf8(const char *Ptr, ssize_t D, char *Start)
{
uchar *p = (uchar*)Ptr;
if (p)
{
if (D >= 0)
{
for (int i=0; i(uchar*)Start; i++)
{
p--;
while (p>(uchar*)Start && IsUtf8_Trail(*p))
p--;
}
}
else
{
// You must pass a start point to move backwards in
// the utf-8 string, otherwise you can run off the
// beginning of the array.
LAssert(0);
}
}
return (char*)p;
}
bool LMatchCharset(short *Map, char16 *Utf, bool &Has8Bit)
{
if (Map && Utf)
{
// Test Charset because we have a map of all the chars in it...
char16 *c;
for (c = Utf; *c; c++)
{
if (*c > 0x7f)
{
// Check char
Has8Bit = true;
int i;
for (i=0; i<128; i++)
{
if (Map[i] == *c)
break;
}
if (i >= 128)
{
// Char not found
return false;
}
}
}
if (Has8Bit)
{
if (!*c)
{
return true;
}
}
}
return false;
}
const char *LUnicodeToCharset(const char *Utf8, ssize_t Len, List *Prefs)
{
const char *Status = "utf-8"; // The default..
LAutoWString Utf((char16*)LNewConvertCp(LGI_WideCharset, Utf8, "utf-8", Len));
if (Utf)
{
if (Prefs)
{
for (auto p: *Prefs)
{
LCharset *Cp = CharsetSystem.GetCsInfo(p);
if (Cp &&
stricmp(Cp->Charset, "us-ascii") != 0 &&
Cp->UnicodeMap)
{
bool Has8Bit = false;
if (LMatchCharset(Cp->UnicodeMap, Utf, Has8Bit))
{
return Cp->Charset;
}
if (!Has8Bit)
{
return "us-ascii";
}
}
}
}
for (LCharset *Cp = LgiCharsets + 1; Cp->Charset; Cp++)
{
if (Cp->UnicodeMap)
{
bool Has8Bit = false;
if (LMatchCharset(Cp->UnicodeMap, Utf, Has8Bit))
{
return Cp->Charset;
}
if (!Has8Bit)
{
return "us-ascii";
}
}
}
}
return Status;
}
LString LToNativeCp(const char *In, ssize_t InLen)
{
const char *Cp = LAnsiToLgiCp();
LString s;
#ifdef WIN32
LCharset *CpInfo = LGetCpInfo(Cp);
if (!CpInfo || CpInfo->Type == CpWindowsDb)
{
if (In)
{
// Double byte charset conversion, don't rely on iconv
// being around to do the conversion.
setlocale(LC_ALL, ".ACP");
if (InLen < 0)
InLen = strlen(In);
LAutoWString Wide(Utf8ToWide(In, InLen));
if (Wide)
{
size_t Converted;
auto Len = wcstombs_s(&Converted, NULL, 0, Wide, 0);
if (s.Length(Len))
{
wcstombs_s(&Converted, s.Get(), Len+1, Wide, Len+1);
s.Get()[Len] = 0;
}
}
}
}
#endif
if (!s)
s = LStrConvertCp(Cp, In, "utf-8", InLen);
return s;
}
LString LFromNativeCp(const char *In, ssize_t InLen)
{
const char *Cp = LAnsiToLgiCp();
LString s;
#ifdef WIN32
LCharset *CpInfo = LGetCpInfo(Cp);
if (!CpInfo || CpInfo->Type == CpWindowsDb)
{
if (In)
{
// Double byte charset conversion, don't rely on iconv
// being around to do the conversion.
setlocale(LC_ALL, ".ACP");
if (InLen < 0)
{
#ifdef __GNUC__
// FIXME
InLen = strlen(In);
#else
InLen = _mbstrlen(In);
#endif
}
else
{
// Work out how many chars 'InLen' bytes is
ssize_t Bytes = InLen;
const char *i = In;
int Chars = 0;
while (*i && Bytes > 0)
{
int n = mblen(i, MB_CUR_MAX);
if (n > 0)
{
Chars++;
Bytes -= n;
i += n;
}
else break;
}
InLen = Chars;
}
size_t Converted;
size_t Len = mbstowcs_s(&Converted, NULL, 0, In, 0);
if (Len)
{
LAutoWString Buf(new char16[Len+1]);
if (Buf)
{
mbstowcs_s(&Converted, Buf, Len, In, Len);
Buf[Len] = 0;
s = Buf;
}
}
}
}
#endif
if (!s)
s = LStrConvertCp("utf-8", In, Cp, InLen);
return s;
}
///////////////////////////////////////////////////////////////////////////
struct LCharsetSystemPriv
{
LCharset *Utf8;
LCharset *Utf16;
LHashTbl, LCharset*> Charsets;
LCharsetSystemPriv() : Charsets(512)
{
Utf8 = 0;
Utf16 = 0;
}
};
LCharsetSystem::LCharsetSystem()
{
char l[256];
// Charset setup, store all the charset pointers
// in a hash table for O(1) lookup.
d = new LCharsetSystemPriv;
LAssert(LgiCharsets->Charset != NULL);
for (LCharset *Cs = LgiCharsets; Cs->Charset; Cs++)
{
strcpy_s(l, sizeof(l), Cs->Charset);
#ifdef _MSC_VER
_strlwr_s(l, sizeof(l));
#else
strlwr(l);
#endif
if (!stricmp(l, "utf-8"))
d->Utf8 = Cs;
else if (!stricmp(l, "utf-16"))
d->Utf16 = Cs;
d->Charsets.Add(l, Cs);
auto a = LString(Cs->AlternateNames).SplitDelimit(",");
for (int n=0; nCharsets.Add(l, Cs);
}
}
}
LCharsetSystem::~LCharsetSystem()
{
DeleteObj(d);
}
LCharset *LCharsetSystem::GetCsInfo(const char *Cp)
{
if (Cp && d)
{
// Lookup the charset in the hash table
char l[256];
strcpy_s(l, sizeof(l), Cp);
#ifdef _MSC_VER
_strlwr_s(l, sizeof(l));
#else
strlwr(l);
#endif
if (!stricmp(l, "utf-8"))
return d->Utf8;
else if (!stricmp(l, "utf-16"))
return d->Utf16;
LCharset *Cs = (LCharset*) d->Charsets.Find(l);
if (Cs)
{
return Cs;
}
else
{
// printf("%s:%i - No charset '%s' in font sub system.\n", __FILE__, __LINE__, l);
// printf("Charsets=%i\n", Charsets->GetUsed());
}
}
return 0;
}
LCharset *LGetCsInfo(const char *Cs)
{
return CharsetSystem.GetCsInfo(Cs);
}
LCharset *LCharsetSystem::GetCsList()
{
return LgiCharsets;
}
LCharset *LGetCsList()
{
return LgiCharsets;
}
diff --git a/src/common/General/Containers.cpp b/src/common/General/Containers.cpp
--- a/src/common/General/Containers.cpp
+++ b/src/common/General/Containers.cpp
@@ -1,835 +1,852 @@
/**
* \file
* \author Matthew Allen
* \date 27/7/1995
* \brief Container classes.\n
Copyright (C) 1995-2003, Matthew Allen
*/
#ifdef LINUX
#define _ISOC99_SOURCE 1
#include
#endif
#include
#include
#include
#include
#include "lgi/common/Mem.h"
#include "lgi/common/Containers.h"
#include "lgi/common/LgiString.h"
#include "lgi/common/LgiCommon.h"
/////////////////////////////////////////////////////////////////////////////////////////////////
int ACmp(LString *a, LString *b)
{
return stricmp(*a, *b);
}
int LCmp(char *a, char *b, NativeInt d)
{
return stricmp(a, b);
}
void UnitTest_CreateList(LArray &a, List &l, int sz = 100)
{
a.Empty();
for (int i=0; i &a, List &l)
{
if (a.Length() != l.Length())
return UnitTest_Err("Wrong size.");
int n = 0;
for (auto s : l)
{
LString t = s;
if (t.Equals(a[n]))
;//printf("%i: %s\n", n, s);
else
return UnitTest_Err("Wrong value.");
n++;
}
return true;
}
#define CHECK(val) if (!(val)) return false
bool UnitTest_ListClass()
{
LArray a;
List l;
// Create test..
UnitTest_CreateList(a, l, 100);
CHECK(UnitTest_Check(a, l));
// Delete tests..
l.Delete(a[3].Get());
a.DeleteAt(3, true);
CHECK(UnitTest_Check(a, l));
while (a.Length() > 60)
{
a.DeleteAt(60, true);
l.DeleteAt(60);
}
CHECK(UnitTest_Check(a, l));
// Sort test..
a.Sort([](auto a, auto b)
{
return stricmp(*a, *b);
});
l.Sort(LCmp);
CHECK(UnitTest_Check(a, l));
return true;
}
////////////////////////////////////////////////////////////////////////////////////////
+#define Q_TRACE(...) if (_debug) LgiTrace(__VA_ARGS__)
+
LMemQueue::LMemQueue(size_t prealloc)
{
PreAlloc = prealloc;
}
LMemQueue::~LMemQueue()
{
Empty();
}
+void LMemQueue::Block::Free()
+{
+ free(this);
+}
+
LMemQueue &LMemQueue::operator=(LMemQueue &p)
{
Empty();
PreAlloc = p.PreAlloc;
for (auto b: p.Mem)
{
int Alloc = sizeof(Block) + b->Size;
Alloc = LGI_ALLOC_ALIGN(Alloc);
Block *n = (Block*) malloc(Alloc);
if (n)
{
memcpy(n, b, sizeof(Block) + b->Size);
Mem.Insert(n);
}
else break;
}
return *this;
}
void LMemQueue::Empty()
{
for (auto b: Mem)
- free(b);
+ {
+ Q_TRACE("Empty() b=%p\n", b);
+ b->Free();
+ }
Mem.Empty();
}
int64 LMemQueue::GetSize()
{
int64 Size = 0;
for (auto b: Mem)
- Size += b->Used - b->Next;
+ Size += b->Length();
return Size;
}
-int64 LMemQueue::Peek(uchar *Ptr, int Size)
+int64 LMemQueue::Peek(uchar *Ptr, ssize_t Size)
{
int64 Status = 0;
if (Ptr && Size <= GetSize())
{
for (auto b: Mem)
{
if (Size <= 0)
break;
- int Copy = MIN(Size, b->Size - b->Next);
+ auto Copy = MIN(Size, b->Length());
if (Copy > 0)
{
- memcpy(Ptr, b->Ptr() + b->Next, Copy);
+ memcpy(Ptr, b->Start(), Copy);
+
Ptr += Copy;
Size -= Copy;
Status += Copy;
}
}
}
return Status;
}
-void *LMemQueue::New(int AddBytes)
+void *LMemQueue::New(ssize_t AddBytes)
{
- int64 Len = GetSize();
- uchar *Data = Len > 0 ? new uchar[Len+AddBytes] : 0;
+ auto Len = GetSize();
+ uchar *Data = Len > 0 ? new uchar[Len + AddBytes] : NULL;
if (Data)
{
ssize_t Rd = Read(Data, Len);
if (Rd >= 0 && AddBytes)
{
- memset(Data+Len, 0, AddBytes);
+ memset(Data + Len, 0, AddBytes);
}
}
return Data;
}
ssize_t LMemQueue::Read(void *Ptr, ssize_t Size, int Flags)
{
- int Status = 0;
+ ssize_t Status = 0;
if (Ptr && Size > 0)
{
for (auto b: Mem)
{
if (Size <= 0)
break;
- int Copy = (int) MIN(Size, b->Used - b->Next);
+ auto Copy = MIN(Size, b->Length());
+ // Q_TRACE("Read, copy=%i\n", (int)Copy);
if (Copy > 0)
{
- memcpy(Ptr, b->Ptr() + b->Next, Copy);
+ memcpy(Ptr, b->Start(), Copy);
((uchar*&)Ptr) += Copy;
Size -= Copy;
- b->Next += Copy;
+ b->Next += (int)Copy;
Status += Copy;
}
}
while (Mem.Length() > 0)
{
auto it = Mem.begin();
auto b = *it;
if (b->Next < b->Used)
break;
Mem.Delete(b);
- free(b);
+ // Q_TRACE("Read, free=%p\n", b);
+ b->Free();
}
}
return Status;
}
-ssize_t LMemQueue::Write(const void *Ptr, ssize_t Size, int Flags)
+ssize_t LMemQueue::Write(const void *_Ptr, ssize_t Size, int Flags)
{
ssize_t Status = 0;
+ auto Ptr = (const uint8_t *)_Ptr;
- if (Ptr && Size > 0)
+ if (!Ptr || Size <= 0)
+ return Status;
+
+ // Is there any space in the last block?
+ auto it = Mem.rbegin();
+ Block *Last = *it;
+ if (Last)
{
- if (PreAlloc > 0)
+ auto Len = MIN(Size, Last->Size - Last->Used);
+ Q_TRACE("Write, last.len=%i\n", (int)Len);
+ if (Len > 0)
{
- auto it = Mem.rbegin();
- Block *Last = *it;
- if (Last)
- {
- int Len = (int) MIN(Size, Last->Size - Last->Used);
- if (Len > 0)
- {
- memcpy(Last->Ptr() + Last->Used, Ptr, Len);
- Last->Used += Len;
- Size -= Len;
- ((uchar*&)Ptr) += Len;
- Status += Len;
- }
- }
+ memcpy(Last->Ptr() + Last->Used, Ptr, Len);
+ Last->Used += (int)Len;
+ LAssert(Last->Used <= Last->Size);
+ Size -= Len;
+ Ptr += Len;
+ Status += Len;
}
+ }
- if (Size > 0)
+ if (Size > 0)
+ {
+ ssize_t Bytes = MAX(PreAlloc, Size);
+ ssize_t Alloc = sizeof(Block) + Bytes;
+ Alloc = LGI_ALLOC_ALIGN(Alloc);
+ Block *b = (Block*) malloc(Alloc);
+ if (b)
{
- ssize_t Bytes = MAX(PreAlloc, Size);
- ssize_t Alloc = sizeof(Block) + Bytes;
- Alloc = LGI_ALLOC_ALIGN(Alloc);
- Block *b = (Block*) malloc(Alloc);
- if (b)
- {
- void *p = b->Ptr();
- memcpy(p, Ptr, Size);
- b->Size = (int)Bytes;
- b->Used = (int)Size;
- b->Next = 0;
- Mem.Insert(b);
- Status += Size;
- }
+ b->Size = (int)Bytes;
+ b->Used = (int)Size;
+ b->Next = 0;
+ Q_TRACE("Write, ptr=%p size=%i\n", b, (int)Size);
+ memcpy(b->Ptr(), Ptr, Size);
+ Mem.Insert(b);
+ Status += Size;
}
}
return Status;
}
ssize_t LMemQueue::Find(LString str, bool caseSensitive)
{
if (!str.Get())
return -1;
if (!caseSensitive)
str = str.Lower();
ssize_t pos = 0;
char *s = str.Get();
ssize_t si = 0;
for (auto b: Mem)
{
auto p = b->Ptr() + b->Next;
auto e = b->Ptr() + b->Used;
while (p < e)
{
if
(
(caseSensitive && (s[si] == *p))
||
(!caseSensitive && (s[si] == ToLower(*p)))
)
{
si++;
if (si == str.Length())
return pos - si + 1;
}
else si = 0;
p++;
pos++;
}
}
return -1;
}
void LMemQueue::Iterate(std::function callback, bool reverse)
{
if (!callback)
{
LAssert(!"No callback.");
return;
}
if (reverse)
{
for (auto it = Mem.rbegin(); it >= Mem.begin(); --it)
{
- auto p = (*it)->Ptr();
- if (!callback(p, (*it)->Used))
+ auto b = (*it);
+ if (!callback(b->Start(), b->Length()))
break;
}
}
else
{
for (auto b: Mem)
{
- auto p = b->Ptr();
- if (!callback(p, b->Used))
+ Q_TRACE("Iter b=%p, blk:%p,%i,%i,%i, start=%p, len=%i\n",
+ b, b->Ptr(), b->Next, b->Used, b->Size,
+ b->Start(), b->Length());
+ if (!callback(b->Start(), b->Length()))
break;
}
}
}
LMemQueue::Buffer LMemQueue::GetBuffer()
{
Buffer b(this);
if (Mem.Length())
{
// Check if last block has space left:
auto it = Mem.rbegin();
b.blk = *it;
if (b.blk->Used < b.blk->Size)
{
b.ptr = b.blk->Ptr() + b.blk->Used;
b.len = b.blk->Size - b.blk->Used;
#ifdef _DEBUG
memset(b.ptr, 0xcd, b.len);
#endif
return b;
}
}
// Otherwise allocate a block
auto sz = PreAlloc > 0 ? PreAlloc : 1024/* some reasonable default? */;
auto alloc = sizeof(Block) + sz;
alloc = LGI_ALLOC_ALIGN(alloc);
b.blk = (Block*) malloc(alloc);
if (b.blk)
{
b.blk->Next = 0;
b.blk->Used = 0;
b.blk->Size = (int)sz;
b.ptr = b.blk->Ptr();
b.len = sz;
#ifdef _DEBUG
memset(b.ptr, 0xcd, b.len);
#endif
Mem.Insert(b.blk);
}
return b;
}
bool LMemQueue::Buffer::Commit(size_t bytes)
{
if (!blk || !ptr)
{
LAssert(!"Invalid param.");
return false;
}
if (blk->Used + bytes > blk->Size)
{
LAssert(!"Wrote too much data?");
blk->Used = blk->Size;
return false;
}
blk->Used += (int)bytes;
return true;
}
//////////////////////////////////////////////////////////////////////////
LString LStringPipe::NewGStr()
{
LString s;
int64 Sz = GetSize();
if (Sz > 0)
{
if (s.Length(Sz))
{
ssize_t Rd = Read(s.Get(), s.Length());
if (Rd > 0 && Rd <= Sz)
{
s.Get()[Rd] = 0;
}
}
else s.Empty();
}
return s;
}
ssize_t LStringPipe::LineChars()
{
ssize_t Len = -1;
for (auto m: Mem)
{
uint8_t *p = m->Ptr();
for (int i = m->Next; i < m->Used; i++)
{
Len++;
if (p[i] == '\n')
return Len;
}
}
return Len;
}
ssize_t LStringPipe::SaveToBuffer(char *Start, ssize_t BufSize, ssize_t Chars)
{
char *Str = Start;
char *End = Str + BufSize; // Not including NULL
auto it = Mem.begin();
Block *m = *it;
while (m)
{
for ( char *MPtr = (char*)m->Ptr();
m->Next < m->Used;
m->Next++)
{
if (Str < End)
*Str++ = MPtr[m->Next];
if (MPtr[m->Next] == '\n')
{
m->Next++;
goto EndPop;
}
}
if (m->Next >= m->Used)
{
Mem.Delete(it);
- free(m);
+ Q_TRACE("SaveToBuffer, free=%p\n", m);
+ m->Free();
it = Mem.begin();
m = *it;
}
}
EndPop:
*Str = 0;
return Str - Start;
}
ssize_t LStringPipe::Pop(LArray &Buf)
{
ssize_t Chars = LineChars();
if (Chars < 0)
return 0;
Chars++; // For the '\n'
if ((ssize_t)Buf.Length() < Chars + 1)
if (!Buf.Length(Chars + 1))
return -1;
SaveToBuffer(Buf.AddressOf(), Chars, Chars);
return Chars;
}
LString LStringPipe::Pop()
{
LString s;
ssize_t Chars = LineChars();
if (Chars < 0)
return s;
s.Length(Chars);
SaveToBuffer(s.Get(), Chars, Chars);
return s;
}
ssize_t LStringPipe::Pop(char *Str, ssize_t BufSize)
{
if (!Str)
return 0;
ssize_t Chars = LineChars();
if (Chars < 0)
return 0;
return SaveToBuffer(Str, BufSize-1 /* for the NULL */, Chars);
}
ssize_t LStringPipe::Push(const char *Str, ssize_t Chars)
{
if (!Str)
return 0;
if (Chars < 0)
Chars = strlen(Str);
return Write((void*)Str, Chars);
}
ssize_t LStringPipe::Push(const char16 *Str, ssize_t Chars)
{
if (!Str)
return 0;
if (Chars < 0)
Chars = StrlenW(Str);
return Write((void*)Str, Chars * sizeof(char16));
}
#ifdef _DEBUG
bool LStringPipe::UnitTest()
{
char Buf[16];
memset(Buf, 0x1, sizeof(Buf));
LStringPipe p(8);
const char s[] = "1234567890abc\n"
"abcdefghijklmn\n";
p.Write(s, sizeof(s)-1);
ssize_t rd = p.Pop(Buf, 10);
int cmp = memcmp(Buf, "123456789\x00\x01\x01\x01\x01\x01\x01", 16);
if (cmp)
return false;
rd = p.Pop(Buf, 10);
cmp = memcmp(Buf, "abcdefghi\x00\x01\x01\x01\x01\x01\x01", 16);
if (cmp)
return false;
p.Empty();
p.Write(s, sizeof(s)-1);
LString r;
r = p.Pop();
if (!r.Equals("1234567890abc"))
return false;
r = p.Pop();
if (!r.Equals("abcdefghijklmn"))
return false;
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
-GMemFile::GMemFile(int BlkSize)
+LMemFile::LMemFile(int BlkSize)
{
CurPos = 0;
Blocks = 0;
BlockSize = BlkSize < 16 ? 16 : BlkSize;
ZeroObj(Local);
}
-GMemFile::~GMemFile()
+LMemFile::~LMemFile()
{
Empty();
}
-GMemFile::Block *GMemFile::Get(int Index)
+LMemFile::Block *LMemFile::Get(int Index)
{
if (Blocks == 0 || Index < 0)
return NULL;
- if (Index < GMEMFILE_BLOCKS)
+ if (Index < LMEMFILE_BLOCKS)
return Local[Index];
- if (Index - GMEMFILE_BLOCKS >= Extra.Length())
+ if (Index - LMEMFILE_BLOCKS >= Extra.Length())
{
LAssert(!"Index beyond last block");
return NULL;
}
- return Extra[Index - GMEMFILE_BLOCKS];
+ return Extra[Index - LMEMFILE_BLOCKS];
}
-GMemFile::Block *GMemFile::Create()
+LMemFile::Block *LMemFile::Create()
{
int Alloc = sizeof(Block) + BlockSize - 1;
Alloc = LGI_ALLOC_ALIGN(Alloc);
Block *b = (Block*) malloc(Alloc);
if (!b)
return NULL;
b->Used = 0;
b->Offset = Blocks * BlockSize;
- if (Blocks < GMEMFILE_BLOCKS)
+ if (Blocks < LMEMFILE_BLOCKS)
Local[Blocks] = b;
else
Extra.Add(b);
Blocks++;
return b;
}
-void GMemFile::Empty()
+void LMemFile::Empty()
{
CurPos = 0;
- for (int i=0; iUsed;
}
-bool GMemFile::FreeBlock(Block *b)
+bool LMemFile::FreeBlock(Block *b)
{
if (!b)
return false;
ssize_t Idx = b->Offset / BlockSize;
- if (Idx < GMEMFILE_BLOCKS)
+ if (Idx < LMEMFILE_BLOCKS)
{
// Local block
if (Local[Idx] != b ||
Idx < Blocks-1)
{
LAssert(!"Block index error.");
return false;
}
free(b);
Local[Idx] = NULL;
Blocks--;
return true;
}
// Extra block
- ssize_t Off = Idx - GMEMFILE_BLOCKS;
+ ssize_t Off = Idx - LMEMFILE_BLOCKS;
if (Off != Extra.Length() - 1)
{
LAssert(!"Block index error.");
return false;
}
free(b);
Extra.DeleteAt(Off, true);
Blocks--;
return true;
}
-int64 GMemFile::SetSize(int64 Size)
+int64 LMemFile::SetSize(int64 Size)
{
if (Size <= 0)
{
Empty();
}
else
{
int64 CurSize = GetSize();
if (Size > CurSize)
{
// Increase size...
int64 Diff = Size - CurSize;
Block *b = GetLast();
if (b->Used < BlockSize)
{
// Add size to last incomplete block
ssize_t Remaining = BlockSize - b->Used;
ssize_t Add = MIN(Diff, Remaining);
b->Used += Add;
Diff -= Add;
}
while (Diff > 0)
{
// Add new blocks to cover the size...
ssize_t Add = MIN(BlockSize, Diff);
b = Create();
b->Used = Add;
Diff -= Add;
}
}
else
{
// Decrease size...
uint64 Diff = CurSize - Size;
while (Diff > 0 && Blocks > 0)
{
Block *b = GetLast();
if (!b)
break;
ssize_t Sub = MIN(b->Used, Diff);
b->Used -= Sub;
Diff -= Sub;
if (b->Used == 0)
FreeBlock(b);
}
}
}
return GetSize();
}
-int64 GMemFile::GetPos()
+int64 LMemFile::GetPos()
{
return CurPos;
}
-int64 GMemFile::SetPos(int64 Pos)
+int64 LMemFile::SetPos(int64 Pos)
{
if (Pos <= 0)
return CurPos = 0; // Off the start of the structure
ssize_t BlockIndex = Pos / BlockSize;
if (BlockIndex >= Blocks)
return CurPos = GetSize(); // Off the end of the structure
if (BlockIndex >= 0 && BlockIndex < Blocks - 1)
return CurPos = Pos; // Inside a full block
Block *Last = GetLast();
uint64 Offset = Pos - Last->Offset;
if (Offset >= Last->Used)
return CurPos = Last->Offset + Last->Used; // End of last block
return CurPos = Pos; // Inside the last block
}
-ssize_t GMemFile::Read(void *Ptr, ssize_t Size, int Flags)
+ssize_t LMemFile::Read(void *Ptr, ssize_t Size, int Flags)
{
if (!Ptr || Size < 1)
return 0;
uint8_t *p = (uint8_t*) Ptr;
uint8_t *end = p + Size;
while (p < end)
{
int Cur = CurBlock();
if (Cur >= Blocks)
break;
Block *b = Get(Cur);
// Where are we in the current block?
ssize_t BlkOffset = CurPos - b->Offset;
LAssert(b && BlkOffset >= 0 && BlkOffset <= (ssize_t)b->Used);
ssize_t Remaining = b->Used - BlkOffset;
if (Remaining > 0)
{
ssize_t Common = MIN(Remaining, end - p);
memcpy(p, b->Data + BlkOffset, Common);
CurPos += Common;
p += Common;
}
else break;
LAssert(p <= end);
if (p >= end) // End of read buffer reached?
break; // Exit loop
}
return p - (uint8_t*) Ptr;
}
-ssize_t GMemFile::Write(const void *Ptr, ssize_t Size, int Flags)
+ssize_t LMemFile::Write(const void *Ptr, ssize_t Size, int Flags)
{
if (!Ptr || Size < 1)
return 0;
uint8_t *p = (uint8_t*) Ptr;
ssize_t len = Size;
Block *b = GetLast();
if (b && b->Used < BlockSize)
{
// Any more space in the last block?
ssize_t Remaining = BlockSize - b->Used;
ssize_t Common = MIN(Remaining, Size);
if (Common > 0)
{
memcpy(b->Data + b->Used, p, Common);
p += Common;
len -= Common;
b->Used += Common;
}
}
// Store remaining data into new blocks
while (len > 0)
{
b = Create();
if (!b)
break;
ssize_t Common = MIN(BlockSize, len);
memcpy(b->Data, p, Common);
b->Used = Common;
p += Common;
len -= Common;
}
return Size - len;
}
diff --git a/src/common/General/DateTime.cpp b/src/common/General/DateTime.cpp
--- a/src/common/General/DateTime.cpp
+++ b/src/common/General/DateTime.cpp
@@ -1,2187 +1,2187 @@
/*
** FILE: LDateTime.cpp
** AUTHOR: Matthew Allen
** DATE: 11/11/98
** DESCRIPTION: Scribe Date Time Object
**
** Copyright (C) 1998, Matthew Allen
** fret@memecode.com
*/
#define _DEFAULT_SOURCE
#include
#include
#include
#include
#include
#if defined(MAC)
#include
#endif
#ifdef WINDOWS
#include
#endif
#include "lgi/common/Lgi.h"
#include "lgi/common/DateTime.h"
#include "lgi/common/DocView.h"
#if !defined(WINDOWS)
constexpr const char *LDateTime::WeekdaysShort[];
constexpr const char *LDateTime::WeekdaysLong[];
constexpr const char *LDateTime::MonthsShort[];
constexpr const char *LDateTime::MonthsLong[];
#define MIN_YEAR 1800
#endif
//////////////////////////////////////////////////////////////////////////////
uint16 LDateTime::DefaultFormat = GDTF_DEFAULT;
char LDateTime::DefaultSeparator = '/';
uint16 LDateTime::GetDefaultFormat()
{
if (DefaultFormat == GDTF_DEFAULT)
{
#ifdef WIN32
TCHAR s[80] = _T("1");
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s));
switch (_tstoi(s))
{
case 0:
DefaultFormat = GDTF_MONTH_DAY_YEAR;
break;
default:
case 1:
DefaultFormat = GDTF_DAY_MONTH_YEAR;
break;
case 2:
DefaultFormat = GDTF_YEAR_MONTH_DAY;
break;
}
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s));
if (_tstoi(s) == 1)
{
DefaultFormat |= GDTF_24HOUR;
}
else
{
DefaultFormat |= GDTF_12HOUR;
}
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s)))
DefaultSeparator = (char)s[0];
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s)))
{
char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 };
LString Str = s;
auto t = Str.SplitDelimit(Sep);
for (int i=0; i= low && (v) <= high)
bool LDateTime::IsValid() const
{
return InRange(_Day, 1, 31) &&
InRange(_Year, 1600, 2100) &&
InRange(_Thousands, 0, 999) &&
InRange(_Month, 1, 12) &&
InRange(_Seconds, 0, 59) &&
InRange(_Minutes, 0, 59) &&
InRange(_Hours, 0, 23) &&
- InRange(_Tz, -720, 720);
+ InRange(_Tz, -780, 780);
}
void LDateTime::SetTimeZone(int NewTz, bool ConvertTime)
{
if (ConvertTime && NewTz != _Tz)
{
// printf("SetTimeZone: %i\n", NewTz - _Tz);
AddMinutes(NewTz - _Tz);
}
_Tz = NewTz;
}
int LDateTime::SystemTimeZone(bool ForceUpdate)
{
if (ForceUpdate || CurTz == NO_ZONE)
{
CurTz = 0;
CurTzOff = 0;
#ifdef MAC
#ifdef LGI_COCOA
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
if (timeZone)
{
NSDate *Now = [NSDate date];
CurTz = (int) [timeZone secondsFromGMTForDate:Now] / 60;
CurTzOff = [timeZone daylightSavingTimeOffsetForDate:Now] / 60;
CurTz -= CurTzOff;
}
#elif defined LGI_CARBON
CFTimeZoneRef tz = CFTimeZoneCopySystem();
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now);
if (dst)
{
CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now);
CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60;
}
else
{
CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60;
}
CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60;
CFRelease(tz);
#endif
#elif defined(WIN32)
timeb tbTime;
ftime(&tbTime);
CurTz = -tbTime.timezone;
TIME_ZONE_INFORMATION Tzi;
if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT)
CurTzOff = -Tzi.DaylightBias;
#elif defined(LINUX) || defined(HAIKU)
int six_months = (365 * 24 * 60 * 60) / 2;
time_t now = 0, then = 0;
time (&now);
then = now - six_months;
tm now_tz, then_tz;
tm *t = localtime_r(&now, &now_tz);
if (t)
{
localtime_r(&then, &then_tz);
CurTz = now_tz.tm_gmtoff / 60;
if (now_tz.tm_isdst)
{
CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60;
CurTz = then_tz.tm_gmtoff / 60;
}
else
// This is not DST so there is no offset right?
CurTzOff = 0; // (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60;
}
else return NO_ZONE;
#else
#error "Impl me."
#endif
}
return CurTz + CurTzOff;
}
int LDateTime::SystemTimeZoneOffset()
{
if (CurTz == NO_ZONE)
SystemTimeZone();
return CurTzOff;
}
#if defined WIN32
LDateTime ConvertSysTime(SYSTEMTIME &st, int year)
{
LDateTime n;
if (st.wYear)
{
n.Year(st.wYear);
n.Month(st.wMonth);
n.Day(st.wDay);
}
else
{
n.Year(year);
n.Month(st.wMonth);
// Find the 'nth' matching weekday, starting from the first day in the month
n.Day(1);
LDateTime c = n;
for (int i=0; iCompare(b);
}
#elif defined(LINUX)
static bool ParseValue(char *s, LAutoString &var, LAutoString &val)
{
if (!s)
return false;
char *e = strchr(s, '=');
if (!e)
return false;
*e++ = 0;
var.Reset(NewStr(s));
val.Reset(NewStr(e));
*e = '=';
return var != 0 && val != 0;
}
#endif
/* Testing code...
LDateTime Start, End;
LArray Info;
Start.Set("1/1/2010");
End.Set("31/12/2014");
LDateTime::GetDaylightSavingsInfo(Info, Start, &End);
LStringPipe p;
for (int i=0; i,int>
{
MonthHash()
{
for (int i=0; i &Info, LDateTime &Start, LDateTime *End)
{
bool Status = false;
#if defined(WIN32)
TIME_ZONE_INFORMATION Tzi;
auto r = GetTimeZoneInformation(&Tzi);
if (r > TIME_ZONE_ID_UNKNOWN)
{
Info.Length(0);
// Find the dates for the previous year from Start. This allows
// us to cover the start of the current year.
LDateTime s = ConvertSysTime(Tzi.StandardDate, Start.Year() - 1);
LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year() - 1);
// Create initial Info entry, as the last change in the previous year
auto *i = &Info.New();
if (s < d)
{
// Year is: Daylight->Standard->Daylight
LDateTime tmp = d;
i->Offset = -(Tzi.Bias + Tzi.DaylightBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();
}
else
{
// Year is: Standard->Daylight->Standard
LDateTime tmp = s;
i->Offset = -(Tzi.Bias + Tzi.StandardBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();;
}
for (auto y=Start.Year(); y<=(End?End->Year():Start.Year()); y++)
{
if (s < d)
{
// Cur year, first event: end of DST
i = &Info.New();
auto tmp = ConvertSysTime(Tzi.StandardDate, y);
i->Offset = -(Tzi.Bias + Tzi.StandardBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();
// Cur year, second event: start of DST
i = &Info.New();
tmp = ConvertSysTime(Tzi.DaylightDate, y);
i->Offset = -(Tzi.Bias + Tzi.DaylightBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();
}
else
{
// Cur year, first event: start of DST
i = &Info.New();
auto tmp = ConvertSysTime(Tzi.DaylightDate, Start.Year());
i->Offset = -(Tzi.Bias + Tzi.DaylightBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();
// Cur year, second event: end of DST
i = &Info.New();
tmp = ConvertSysTime(Tzi.StandardDate, Start.Year());
i->Offset = -(Tzi.Bias + Tzi.StandardBias);
tmp.AddMinutes(-i->Offset);
i->UtcTimeStamp = tmp.Ts();
}
}
Status = true;
}
#elif defined(MAC)
LDateTime Before = Start;
Before.AddMonths(-6);
NSTimeZone *tz = [NSTimeZone systemTimeZone];
NSDate *startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(Before.Ts() / Second64Bit) - Offset1800];
for (int n=0; n<6; n++)
{
NSDate *next = [tz nextDaylightSavingTimeTransitionAfterDate:startDate];
auto &i = Info.New();
i.UtcTimeStamp = ([next timeIntervalSince1970] + Offset1800) * Second64Bit;
i.Offset = (int)([tz secondsFromGMTForDate:[next dateByAddingTimeInterval:60]]/60);
#if DEBUG_DST_INFO
{
LDateTime dt;
dt.Set(i.UtcTimeStamp);
LgiTrace("%s:%i - Ts=%s Off=%i\n", _FL, dt.Get().Get(), i.Offset);
}
#endif
[startDate release];
startDate = next;
}
#elif defined(LINUX)
if (!Zdump.Length())
{
FILE *f = popen("zdump -v /etc/localtime", "r");
if (f)
{
char s[256];
size_t r;
LStringPipe p(1024);
while ((r = fread(s, 1, sizeof(s), f)) > 0)
{
p.Write(s, (int)r);
}
fclose(f);
LString ps = p.NewGStr();
Zdump = ps.Split("\n");
}
}
MonthHash Lut;
LDateTime Prev;
int PrevOff = 0;
for (auto Line: Zdump)
{
auto l = Line.SplitDelimit(" \t");
if (l.Length() >= 16 &&
l[0].Equals("/etc/localtime"))
{
// /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#if DEBUG_DST_INFO
printf("DST: %s\n", Line);
#endif
LDateTime Utc;
Utc.Year(l[5].Int());
auto Tm = l[4].SplitDelimit(":");
if (Tm.Length() != 3)
{
#if DEBUG_DST_INFO
printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4], Line);
#endif
continue;
}
Utc.Hours(Tm[0].Int());
Utc.Minutes(Tm[1].Int());
Utc.Seconds(Tm[2].Int());
if (Utc.Minutes() < 0)
{
#if DEBUG_DST_INFO
printf("%s:%i - Mins is zero: %s\n", _FL, l[4]);
#endif
continue;
}
int m = Lut.Find(l[2]);
if (!m)
{
#if DEBUG_DST_INFO
printf("%s:%i - Unknown month '%s'\n", _FL, l[2]);
#endif
continue;
}
Utc.Day(l[3].Int());
Utc.Month(m);
LAutoString Var, Val;
if (!ParseValue(l[14], Var, Val) ||
stricmp(Var, "isdst"))
{
#if DEBUG_DST_INFO
printf("%s:%i - Unknown value for isdst\n", _FL);
#endif
continue;
}
if (!ParseValue(l[15], Var, Val) ||
stricmp(Var, "gmtoff"))
{
#if DEBUG_DST_INFO
printf("%s:%i - Unknown value for isdst\n", _FL);
#endif
continue;
}
int Off = atoi(Val) / 60;
if (Utc.Ts() == 0)
continue;
if (Prev.Year() &&
Prev < Start &&
Start < Utc)
{
// Emit initial entry for 'start'
auto &inf = Info.New();
inf.UtcTimeStamp = Prev;
inf.Offset = PrevOff;
#if DEBUG_DST_INFO
printf("Info: Start=%s %i\n", Prev.Get().Get(), inf.Offset);
#endif
}
if (Utc > Start)
{
// Emit furthur entries for DST events between start and end.
auto &inf = Info.New();
inf.UtcTimeStamp = Utc;
inf.Offset = Off;
#if DEBUG_DST_INFO
printf("Info: Next=%s %i\n", Utc.Get().Get(), inf.Offset);
#endif
if (End && Utc > *End)
{
// printf("Utc after end: %s > %s\n", Utc.Get().Get(), End->Get().Get());
break;
}
}
Prev = Utc;
PrevOff = Off;
}
}
Status = Info.Length() > 1;
#else
LAssert(!"Not implemented.");
#endif
return Status;
}
bool LDateTime::DstToLocal(LArray &Dst, LDateTime &dt)
{
if (dt.GetTimeZone())
{
LAssert(!"Should be a UTC date.");
return true;
}
// LgiTrace("DstToLocal: %s\n", dt.Get().Get());
LAssert(Dst.Length() > 1); // Needs to have at least 2 entries...?
for (size_t i=0; i %s\n", (int)i, start.Get().Get(), end.Get().Get());
if (dt >= start && dt < end)
{
dt.SetTimeZone(a.Offset, true);
return true;
}
}
auto Last = Dst.Last();
LDateTime d;
d.Set(Last.UtcTimeStamp);
if (dt >= d && dt.Year() == d.Year())
{
// If it's after the last DST change but in the same year... it's ok...
// Just use the last offset.
dt.SetTimeZone(Last.Offset, true);
return true;
}
LAssert(!"No valid DST range for this date.");
return false;
}
int LDateTime::DayOfWeek() const
{
int Index = 0;
int Day = IsLeapYear() ? 29 : 28;
switch (_Year / 100)
{
case 19:
{
Index = 3;
break;
}
case 20:
{
Index = 2;
break;
}
}
// get year right
int y = _Year % 100;
int r = y % 12;
Index = (Index + (y / 12) + r + (r / 4)) % 7;
// get month right
if (_Month % 2 == 0)
{
// even month
if (_Month > 2) Day = _Month;
}
else
{
// odd month
switch (_Month)
{
case 1:
{
Day = 31;
if (IsLeapYear())
{
Index = Index > 0 ? Index - 1 : Index + 6;
}
break;
}
case 11:
case 3:
{
Day = 7;
break;
}
case 5:
{
Day = 9;
break;
}
case 7:
{
Day = 11;
break;
}
case 9:
{
Day = 5;
break;
}
}
}
// get day right
int Diff = Index - (Day - _Day);
while (Diff < 0) Diff += 7;
return Diff % 7;
}
LDateTime LDateTime::Now()
{
LDateTime dt;
dt.SetNow();
return dt;
}
LDateTime &LDateTime::SetNow()
{
#ifdef WIN32
SYSTEMTIME stNow;
FILETIME ftNow;
GetSystemTime(&stNow);
SystemTimeToFileTime(&stNow, &ftNow);
uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime;
Set(i64);
#else
time_t now;
time(&now);
struct tm *time = localtime(&now);
if (time)
*this = time;
#ifndef LGI_STATIC
else
{
LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now);
}
#endif
#endif
return *this;
}
#define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) )
#define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" )
LString LDateTime::GetDate() const
{
char s[32];
int Ch = GetDate(s, sizeof(s));
return LString(s, Ch);
}
int LDateTime::GetDate(char *Str, size_t SLen) const
{
int Ch = 0;
if (Str && SLen > 0)
{
switch (_Format & GDTF_DATE_MASK)
{
case GDTF_MONTH_DAY_YEAR:
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month);
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day);
Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year);
break;
default:
case GDTF_DAY_MONTH_YEAR:
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day);
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month);
Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year);
break;
case GDTF_YEAR_MONTH_DAY:
Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year);
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month);
Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day);
break;
}
}
return Ch;
}
LString LDateTime::GetTime() const
{
char s[32];
int Ch = GetTime(s, sizeof(s));
return LString(s, Ch);
}
int LDateTime::GetTime(char *Str, size_t SLen) const
{
int Ch = 0;
if (Str && SLen > 0)
{
switch (_Format & GDTF_TIME_MASK)
{
case GDTF_12HOUR:
default:
{
Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours));
break;
}
case GDTF_24HOUR:
{
Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds);
break;
}
}
}
return Ch;
}
uint64 LDateTime::Ts() const
{
uint64 ts = 0;
Get(ts);
return ts;
}
bool LDateTime::SetUnix(uint64 s)
{
#if defined(WINDOWS)
return Set(s * LDateTime::Second64Bit + 116445168000000000LL);
#else
return Set((s + Offset1800) * LDateTime::Second64Bit);
#endif
}
bool LDateTime::Set(uint64 s)
{
#if defined WIN32
FILETIME Utc;
SYSTEMTIME System;
// Adjust to the desired timezone
uint64 u = s + ((int64)_Tz * 60 * Second64Bit);
Utc.dwHighDateTime = u >> 32;
Utc.dwLowDateTime = u & 0xffffffff;
if (!FileTimeToSystemTime(&Utc, &System))
return false;
_Year = System.wYear;
_Month = System.wMonth;
_Day = System.wDay;
_Hours = System.wHour;
_Minutes = System.wMinute;
_Seconds = System.wSecond;
_Thousands = System.wMilliseconds;
return true;
#else
time_t t = (time_t) (((int64)(s / Second64Bit)) - Offset1800);
Set(t);
_Thousands = s % Second64Bit;
return true;
#endif
}
bool LDateTime::Set(time_t tt)
{
struct tm *t;
#if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005
if (_Tz)
tt += _Tz * 60;
t = gmtime(&tt);
if (t)
#else
struct tm tmp;
if (_localtime64_s(t = &tmp, &tt) == 0)
#endif
{
_Year = t->tm_year + 1900;
_Month = t->tm_mon + 1;
_Day = t->tm_mday;
_Hours = t->tm_hour;
_Minutes = t->tm_min;
_Seconds = t->tm_sec;
_Thousands = 0;
// _Tz = SystemTimeZone();
return true;
}
return false;
}
uint64_t LDateTime::OsTime() const
{
#ifdef WINDOWS
FILETIME Utc;
SYSTEMTIME System;
System.wYear = _Year;
System.wMonth = limit(_Month, 1, 12);
System.wDay = limit(_Day, 1, 31);
System.wHour = limit(_Hours, 0, 23);
System.wMinute = limit(_Minutes, 0, 59);
System.wSecond = limit(_Seconds, 0, 59);
System.wMilliseconds = limit(_Thousands, 0, 999);
System.wDayOfWeek = DayOfWeek();
if (SystemTimeToFileTime(&System, &Utc))
{
uint64_t s = ((uint64_t)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime;
if (_Tz)
// Adjust for timezone
s -= (int64)_Tz * 60 * Second64Bit;
return s;
}
else
{
DWORD Err = GetLastError();
LAssert(!"SystemTimeToFileTime failed.");
}
#else
if (_Year < MIN_YEAR)
return 0;
struct tm t;
ZeroObj(t);
t.tm_year = _Year - 1900;
t.tm_mon = _Month - 1;
t.tm_mday = _Day;
t.tm_hour = _Hours;
t.tm_min = _Minutes;
t.tm_sec = _Seconds;
t.tm_isdst = -1;
time_t sec = timegm(&t);
if (sec == -1)
return 0;
if (_Tz)
{
// Adjust the output to UTC from the current timezone.
sec -= _Tz * 60;
}
return sec;
#endif
return 0;
}
bool LDateTime::OsTime(uint64_t ts)
{
return Set((time_t)ts);
}
bool LDateTime::Get(uint64 &s) const
{
#ifdef WINDOWS
if (!IsValid())
{
LAssert(!"Needs a valid date.");
return false;
}
s = OsTime();
if (!s)
return false;
return true;
#else
if (_Year < MIN_YEAR)
return false;
auto sec = OsTime();
s = (uint64)(sec + Offset1800) * Second64Bit + _Thousands;
return true;
#endif
}
LString LDateTime::Get() const
{
char buf[32];
int Ch = GetDate(buf, sizeof(buf));
buf[Ch++] = ' ';
Ch += GetTime(buf+Ch, sizeof(buf)-Ch);
return LString(buf, Ch);
}
void LDateTime::Get(char *Str, size_t SLen) const
{
if (Str)
{
GetDate(Str, SLen);
size_t len = strlen(Str);
if (len < SLen - 1)
{
Str[len++] = ' ';
GetTime(Str+len, SLen-len);
}
}
}
bool LDateTime::Set(const char *Str)
{
if (!Str)
return false;
char Local[256];
strcpy_s(Local, sizeof(Local), Str);
char *Sep = strchr(Local, ' ');
if (Sep)
{
*Sep++ = 0;
if (!SetTime(Sep))
return false;
}
if (!SetDate(Local))
return false;
return true;
}
void LDateTime::Month(char *m)
{
int i = IsMonth(m);
if (i >= 0)
_Month = i + 1;
}
int DateComponent(const char *s)
{
int64 i = Atoi(s);
return i ? (int)i : LDateTime::IsMonth(s);
}
bool LDateTime::SetDate(const char *Str)
{
bool Status = false;
if (Str)
{
auto T = LString(Str).SplitDelimit("/-.,_\\");
if (T.Length() == 3)
{
int i[3] = { DateComponent(T[0]), DateComponent(T[1]), DateComponent(T[2]) };
int fmt = _Format & GDTF_DATE_MASK;
// Do some guessing / overrides.
// Don't let _Format define the format completely.
if (i[0] > 1000)
{
fmt = GDTF_YEAR_MONTH_DAY;
}
else if (i[2] > 1000)
{
if (i[0] > 12)
fmt = GDTF_DAY_MONTH_YEAR;
else if (i[1] > 12)
fmt = GDTF_MONTH_DAY_YEAR;
}
switch (fmt)
{
case GDTF_MONTH_DAY_YEAR:
{
_Month = i[0];
_Day = i[1];
_Year = i[2];
break;
}
case GDTF_DAY_MONTH_YEAR:
{
_Day = i[0];
_Month = i[1];
_Year = i[2];
break;
}
case GDTF_YEAR_MONTH_DAY:
{
_Year = i[0];
_Month = i[1];
_Day = i[2];
break;
}
default:
{
_Year = i[2];
if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR)
{
// Assume m/d/yyyy
_Day = i[1];
_Month = i[0];
}
else
{
// Who knows???
// Assume d/m/yyyy
_Day = i[0];
_Month = i[1];
}
break;
}
}
if (_Year < 100)
{
LAssert(_Day < 1000 && _Month < 1000);
if (_Year >= 80)
_Year += 1900;
else
_Year += 2000;
}
Status = true;
}
else
{
// Fall back to fuzzy matching
auto T = LString(Str).SplitDelimit(" ,");
MonthHash Lut;
int FMonth = 0;
int FDay = 0;
int FYear = 0;
for (unsigned i=0; i 0)
{
if (i >= 1000)
{
FYear = i;
}
else if (i < 32)
{
FDay = i;
}
}
}
else
{
int i = Lut.Find(p);
if (i)
FMonth = i;
}
}
if (FMonth && FDay)
{
Day(FDay);
Month(FMonth);
}
if (FYear)
{
Year(FYear);
}
else
{
LDateTime Now;
Now.SetNow();
Year(Now.Year());
}
}
}
return Status;
}
bool LDateTime::SetTime(const char *Str)
{
if (!Str)
return false;
auto T = LString(Str).SplitDelimit(":.");
if (T.Length() < 2 || T.Length() > 4)
return false;
_Hours = (int)T[0].Int();
_Minutes = (int)T[1].Int();
if (_Hours < 0 || _Minutes < 0)
return false;
char *s = T[2];
if (s) _Seconds = atoi(s);
else _Seconds = 0;
_Thousands = 0;
s = T.Last();
if (s)
{
if (strchr(s, 'p') || strchr(s, 'P'))
{
if (_Hours != 12)
_Hours += 12;
}
else if (strchr(s, 'a') || strchr(s, 'A'))
{
if (_Hours == 12)
_Hours -= 12;
}
}
if (T.Length() > 3)
{
LString t = "0.";
t += s;
_Thousands = (int) (t.Float() * 1000);
}
return true;
}
int LDateTime::IsWeekDay(const char *s)
{
for (unsigned n=0; n= 4)
{
Year((int)t[0].Int());
Month((int)t[1].Int());
Day((int)t[2].Int());
}
else if (t[2].Length() >= 4)
{
Day((int)t[0].Int());
Month((int)t[1].Int());
Year((int)t[2].Int());
}
else
{
LAssert(!"Unknown date format?");
return false;
}
}
}
else if (a[i].Length() == 4)
Year((int)a[i].Int());
else if (!Day())
Day((int)a[i].Int());
}
else if (IsAlpha(*c))
{
int WkDay = IsWeekDay(c);
if (WkDay >= 0)
continue;
int Mnth = IsMonth(c);
if (Mnth >= 0)
Month(Mnth + 1);
}
else if (*c == '-' || *c == '+')
{
c++;
if (strlen(c) == 4)
{
// Timezone..
int64 Tz = a[i].Int();
int Hrs = (int) (Tz / 100);
int Min = (int) (Tz % 100);
SetTimeZone(Hrs * 60 + Min, false);
}
}
}
return IsValid();
}
int LDateTime::Sizeof()
{
return sizeof(int) * 7;
}
bool LDateTime::Serialize(LFile &f, bool Write)
{
int32 i;
if (Write)
{
#define wf(fld) i = fld; f << i;
wf(_Day);
wf(_Month);
wf(_Year);
wf(_Thousands);
wf(_Seconds);
wf(_Minutes);
wf(_Hours);
}
else
{
#define rf(fld) f >> i; fld = i;
rf(_Day);
rf(_Month);
rf(_Year);
rf(_Thousands);
rf(_Seconds);
rf(_Minutes);
rf(_Hours);
}
return true;
}
/*
bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write)
{
#ifndef LGI_STATIC
if (Props && Name)
{
struct _Date
{
uint8_t Day;
uint8_t Month;
int16_t Year;
uint8_t Hour;
uint8_t Minute;
uint16_t ThouSec;
};
LAssert(sizeof(_Date) == 8);
if (Write)
{
_Date d;
d.Day = _Day;
d.Month = _Month;
d.Year = _Year;
d.Hour = _Hours;
d.Minute = _Minutes;
d.ThouSec = (_Seconds * 1000) + _Thousands;
return Props->Set(Name, &d, sizeof(d));
}
else // Read
{
void *Ptr;
int Len;
if (Props->Get(Name, Ptr, Len) &&
sizeof(_Date) == Len)
{
_Date *d = (_Date*) Ptr;
_Day = d->Day;
_Month = d->Month;
_Year = d->Year;
_Hours = d->Hour;
_Minutes = d->Minute;
_Seconds = d->ThouSec / 1000;
_Thousands = d->ThouSec % 1000;
return true;
}
}
}
#endif
return false;
}
*/
int LDateTime::Compare(const LDateTime *Date) const
{
// this - *Date
auto ThisTs = IsValid() ? Ts() : 0;
auto DateTs = Date->IsValid() ? Date->Ts() : 0;
// If these ever fire, the cast to int64_t will overflow
LAssert((ThisTs & 0x800000000000000) == 0);
LAssert((DateTs & 0x800000000000000) == 0);
int64_t Diff = (int64_t)ThisTs - DateTs;
if (Diff < 0)
return -1;
return Diff > 0 ? 1 : 0;
}
#define DATETIME_OP(op) \
bool LDateTime::operator op(const LDateTime &dt) const \
{ \
auto a = Ts(); \
auto b = dt.Ts(); \
return a op b; \
}
DATETIME_OP(<)
DATETIME_OP(<=)
DATETIME_OP(>)
DATETIME_OP(>=)
bool LDateTime::operator ==(const LDateTime &dt) const
{
return _Year == dt._Year &&
_Month == dt._Month &&
_Day == dt._Day &&
_Hours == dt._Hours &&
_Minutes == dt._Minutes &&
_Seconds == dt._Seconds &&
_Thousands == dt._Thousands;
}
bool LDateTime::operator !=(const LDateTime &dt) const
{
return _Year != dt._Year ||
_Month != dt._Month ||
_Day != dt._Day ||
_Hours != dt._Hours ||
_Minutes != dt._Minutes ||
_Seconds != dt._Seconds ||
_Thousands != dt._Thousands;
}
int LDateTime::DiffMonths(const LDateTime &dt)
{
int a = (Year() * 12) + Month();
int b = (dt.Year() * 12) + dt.Month();
return b - a;
}
LDateTime LDateTime::operator -(const LDateTime &dt)
{
uint64 a, b;
Get(a);
dt.Get(b);
/// Resolution of a second when using 64 bit timestamps
int64 Sec = Second64Bit;
int64 Min = 60 * Sec;
int64 Hr = 60 * Min;
int64 Day = 24 * Hr;
int64 d = (int64)a - (int64)b;
LDateTime r;
r._Day = (int16) (d / Day);
d -= r._Day * Day;
r._Hours = (int16) (d / Hr);
d -= r._Hours * Hr;
r._Minutes = (int16) (d / Min);
d -= r._Minutes * Min;
r._Seconds = (int16) (d / Sec);
#ifdef WIN32
d -= r._Seconds * Sec;
r._Thousands = (int16) (d / 10000);
#else
r._Thousands = 0;
#endif
return r;
}
LDateTime LDateTime::operator +(const LDateTime &dt)
{
LDateTime s = *this;
s.AddMonths(dt.Month());
s.AddDays(dt.Day());
s.AddHours(dt.Hours());
s.AddMinutes(dt.Minutes());
// s.AddSeconds(dt.Seconds());
return s;
}
LDateTime &LDateTime::operator =(const LDateTime &t)
{
_Day = t._Day;
_Year = t._Year;
_Thousands = t._Thousands;
_Month = t._Month;
_Seconds = t._Seconds;
_Minutes = t._Minutes;
_Hours = t._Hours;
_Tz = t._Tz;
_Format = t._Format;
return *this;
}
LDateTime &LDateTime::operator =(struct tm *time)
{
if (time)
{
_Seconds = time->tm_sec;
_Minutes = time->tm_min;
_Hours = time->tm_hour;
_Day = time->tm_mday;
_Month = time->tm_mon + 1;
_Year = time->tm_year + 1900;
}
else Empty();
return *this;
}
bool LDateTime::IsSameDay(LDateTime &d) const
{
return Day() == d.Day() &&
Month() == d.Month() &&
Year() == d.Year();
}
bool LDateTime::IsSameMonth(LDateTime &d) const
{
return Day() == d.Day() &&
Month() == d.Month();
}
bool LDateTime::IsSameYear(LDateTime &d) const
{
return Year() == d.Year();
}
bool LDateTime::IsLeapYear(int Year) const
{
if (Year < 0) Year = _Year;
if (Year % 4 != 0)
{
return false;
}
if (Year % 400 == 0)
{
return true;
}
if (Year % 100 == 0)
{
return false;
}
return true;
}
int LDateTime::DaysInMonth() const
{
if (_Month == 2 &&
IsLeapYear())
{
return 29;
}
short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0;
}
#define MinutesInDay (60*24)
void LDateTime::AddSeconds(int64 Seconds)
{
uint64 i;
if (Get(i))
{
i += Seconds * Second64Bit;
Set(i);
}
}
void LDateTime::AddMinutes(int64 Minutes)
{
uint64 i;
if (Get(i))
{
int64 delta = Minutes * 60 * Second64Bit;
uint64 n = i + delta;
// printf("AddMin " LPrintfInt64 " + " LPrintfInt64 " = " LPrintfInt64 "\n", i, delta, n);
Set(n);
#if 0
uint64 i2;
Get(i2);
int64 diff = (int64)i2-(int64)i;
#endif
}
}
void LDateTime::AddHours(int64 Hours)
{
uint64 i;
if (Get(i))
{
i += Hours * LDateTime::HourLength * Second64Bit;
Set(i);
}
}
bool LDateTime::AddDays(int64 Days)
{
if (!Days)
return true;
uint64 Ts;
if (!Get(Ts))
return false;
Ts += Days * LDateTime::DayLength * Second64Bit;
bool b = Set(Ts);
return b;
}
void LDateTime::AddMonths(int64 Months)
{
int64 m = _Month + Months;
do
{
if (m < 1)
{
_Year--;
m += 12;
}
else if (m > 12)
{
_Year++;
m -= 12;
}
else
{
break;
}
}
while (1);
_Month = (int16) m;
if (_Day > DaysInMonth())
_Day = DaysInMonth();
}
LString LDateTime::DescribePeriod(double seconds)
{
int mins = (int) (seconds / 60);
seconds -= mins * 60;
int hrs = mins / 60;
mins -= hrs * 60;
int days = hrs / 24;
hrs -= days * 24;
LString s;
if (days > 0)
s.Printf("%id %ih %im %is", days, hrs, mins, (int)seconds);
else if (hrs > 0)
s.Printf("%ih %im %is", hrs, mins, (int)seconds);
else if (mins > 0)
s.Printf("%im %is", mins, (int)seconds);
else
s.Printf("%is", (int)seconds);
return s;
}
LString LDateTime::DescribePeriod(LDateTime to)
{
auto ThisTs = Ts();
auto ToTs = to.Ts();
auto diff = ThisTs < ToTs ? ToTs - ThisTs : ThisTs - ToTs;
auto seconds = (double)diff / LDateTime::Second64Bit;
return DescribePeriod(seconds);
}
int LDateTime::MonthFromName(const char *Name)
{
if (Name)
{
for (int m=0; m<12; m++)
{
if (strnicmp(Name, MonthsShort[m], strlen(MonthsShort[m])) == 0)
{
return m + 1;
break;
}
}
}
return -1;
}
bool LDateTime::Decode(const char *In)
{
// Test data:
//
// Tue, 6 Dec 2005 1:25:32 -0800
Empty();
if (!In)
{
LAssert(0);
return false;
}
bool Status = false;
// Tokenize delimited by whitespace
LString::Array T = LString(In).SplitDelimit(", \t\r\n");
if (T.Length() < 2)
{
if (T[0].IsNumeric())
{
// Some sort of timestamp?
uint64_t Ts = Atoi(T[0].Get());
if (Ts > 0)
{
return SetUnix(Ts);
}
else return false;
}
else
{
// What now?
return false;
}
}
else
{
bool GotDate = false;
for (unsigned i=0; i 31)
{
// Y/M/D?
Year((int)Date[0].Int());
Day((int)Date[2].Int());
}
else if (Date[2].Int() > 31)
{
// D/M/Y?
Day((int)Date[0].Int());
Year((int)Date[2].Int());
}
else
{
// Ambiguous year...
bool YrFirst = true;
if (Date[0].Length() == 1)
YrFirst = false;
// else we really can't tell.. just go with year first
if (YrFirst)
{
Year((int)Date[0].Int());
Day((int)Date[2].Int());
}
else
{
Day((int)Date[0].Int());
Year((int)Date[2].Int());
}
LDateTime Now;
Now.SetNow();
if (Year() + 2000 <= Now.Year())
Year(2000 + Year());
else
Year(1900 + Year());
}
if (Date[1].IsNumeric())
Month((int)Date[1].Int());
else
{
int m = MonthFromName(Date[1]);
if (m > 0)
Month(m);
}
GotDate = true;
Status = true;
}
else if (s.Find(":") >= 0)
{
// whole time
// Do some validation
bool Valid = true;
for (char *c = s; *c && Valid; c++)
{
if (!(IsDigit(*c) || *c == ':'))
Valid = false;
}
if (Valid)
{
LString::Array Time = s.Split(":");
if (Time.Length() == 2 ||
Time.Length() == 3)
{
// Hour
int i = (int) Time[0].Int();
if (i >= 0)
Hours(i);
if (s.Lower().Find("p") >= 0)
{
if (Hours() < 12)
Hours(Hours() + 12);
}
// Minute
i = (int) Time[1].Int();
if (i >= 0)
Minutes(i);
if (Time.Length() == 3)
{
// Second
i = (int) Time[2].Int();
if (i >= 0)
Seconds(i);
}
Status = true;
}
}
}
else if (IsAlpha(s(0)))
{
// text
int m = MonthFromName(s);
if (m > 0)
Month(m);
}
else if (strchr("+-", *s))
{
// timezone
DoTimeZone:
LDateTime Now;
double OurTmz = (double)Now.SystemTimeZone() / 60;
if (s &&
strchr("-+", *s) &&
strlen(s) == 5)
{
#if 1
int i = atoi(s);
int hr = i / 100;
int min = i % 100;
SetTimeZone(hr * 60 + min, false);
#else
// adjust for timezone
char Buf[32];
memcpy(Buf, s, 3);
Buf[3] = 0;
double TheirTmz = atof(Buf);
memcpy(Buf+1, s + 3, 2);
TheirTmz += (atof(Buf) / 60);
if (Tz)
{
*Tz = TheirTmz;
}
double AdjustHours = OurTmz - TheirTmz;
AddMinutes((int) (AdjustHours * 60));
#endif
}
else
{
// assume GMT
AddMinutes((int) (OurTmz * 60));
}
}
else if (s.IsNumeric())
{
int Count = 0;
for (char *c = s; *c; c++)
{
if (!IsDigit(*c))
break;
Count++;
}
if (Count <= 2)
{
if (Day())
{
// We already have a day... so this might be
// a 2 digit year...
LDateTime Now;
Now.SetNow();
int Yr = atoi(s);
if (2000 + Yr <= Now.Year())
Year(2000 + Yr);
else
Year(1900 + Yr);
}
else
{
// A day number (hopefully)?
Day((int)s.Int());
}
}
else if (Count == 4)
{
if (!Year())
{
// A year!
Year((int)s.Int());
Status = true;
}
else
{
goto DoTimeZone;
}
// My one and only Y2K fix
// d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr);
}
}
}
}
return Status;
}
bool LDateTime::GetVariant(const char *Name, LVariant &Dst, char *Array)
{
LDomProperty p = LStringToDomProp(Name);
switch (p)
{
case DateYear: // Type: Int32
Dst = Year();
break;
case DateMonth: // Type: Int32
Dst = Month();
break;
case DateDay: // Type: Int32
Dst = Day();
break;
case DateHour: // Type: Int32
Dst = Hours();
break;
case DateMinute: // Type: Int32
Dst = Minutes();
break;
case DateSecond: // Type: Int32
Dst = Seconds();
break;
case DateDate: // Type: String
{
char s[32];
GetDate(s, sizeof(s));
Dst = s;
break;
}
case DateTime: // Type: String
{
char s[32];
GetTime(s, sizeof(s));
Dst = s;
break;
}
case TypeString: // Type: String
case DateDateAndTime: // Type: String
{
char s[32];
Get(s, sizeof(s));
Dst = s;
break;
}
case TypeInt: // Type: Int64
case DateTimestamp: // Type: Int64
{
uint64 i = 0;
Get(i);
Dst = (int64)i;
break;
}
case DateSecond64Bit:
{
Dst = Second64Bit;
break;
}
default:
{
return false;
}
}
return true;
}
bool LDateTime::SetVariant(const char *Name, LVariant &Value, char *Array)
{
LDomProperty p = LStringToDomProp(Name);
switch (p)
{
case DateYear:
Year(Value.CastInt32());
break;
case DateMonth:
Month(Value.CastInt32());
break;
case DateDay:
Day(Value.CastInt32());
break;
case DateHour:
Hours(Value.CastInt32());
break;
case DateMinute:
Minutes(Value.CastInt32());
break;
case DateSecond:
Seconds(Value.CastInt32());
break;
case DateDate:
SetDate(Value.Str());
break;
case DateTime:
SetTime(Value.Str());
break;
case DateDateAndTime:
Set(Value.Str());
break;
case DateTimestamp:
Set((uint64)Value.CastInt64());
break;
default:
return false;
}
return true;
}
bool LDateTime::CallMethod(const char *Name, LVariant *ReturnValue, LArray &Args)
{
switch (LStringToDomProp(Name))
{
case DateSetNow:
SetNow();
if (ReturnValue)
*ReturnValue = true;
break;
case DateSetStr:
if (Args.Length() < 1)
return false;
bool Status;
if (Args[0]->Type == GV_INT64)
Status = Set((uint64) Args[0]->Value.Int64);
else
Status = Set(Args[0]->Str());
if (ReturnValue)
*ReturnValue = Status;
break;
case DateGetStr:
{
char s[256] = "";
Get(s, sizeof(s));
if (ReturnValue)
*ReturnValue = s;
break;
}
default:
return false;
}
return true;
}
#ifdef _DEBUG
#define DATE_ASSERT(i) \
if (!(i)) \
{ \
LAssert(!"LDateTime unit test failed."); \
return false; \
}
bool LDateTime_Test()
{
// Check 64bit get/set
LDateTime t("1/1/2017 0:0:0");
uint64 i;
DATE_ASSERT(t.Get(i));
LgiTrace("Get='%s'\n", t.Get().Get());
uint64 i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit);
LDateTime t2;
t2.SetFormat(GDTF_DAY_MONTH_YEAR);
t2.Set(i2);
LString s = t2.Get();
LgiTrace("Set='%s'\n", s.Get());
DATE_ASSERT(!stricmp(s, "2/1/2017 12:00:00a") ||
!stricmp(s, "2/01/2017 12:00:00a"));
t.SetNow();
LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours());
t2 = t;
t2.ToUtc();
LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours());
t2.ToLocal();
LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours());
DATE_ASSERT(t == t2);
return true;
}
#endif
diff --git a/src/common/Lgi/MemStream.cpp b/src/common/Lgi/MemStream.cpp
--- a/src/common/Lgi/MemStream.cpp
+++ b/src/common/Lgi/MemStream.cpp
@@ -1,354 +1,379 @@
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Stream.h"
//////////////////////////////////////////////////////////////////////////////
void LMemStream::_Init()
{
GrowBlockSize = 0;
Len = Pos = Alloc = 0;
Mem = 0;
Own = true;
}
LMemStream::LMemStream()
{
_Init();
}
LMemStream::LMemStream(LStreamI *Src, int64 Start, int64 len)
{
_Init();
if (Src)
{
bool Error = false;
if (Start >= 0)
{
- int64 Result = Src->SetPos(Start);
- if (Result >= 0 && Result != Start)
+ auto Result = Src->SetPos(Start);
+ if (Result < 0)
+ {
+ // Stream doesn't support streaming...
+ // Read enough bytes to find the start...
+ char buf[1024];
+ for (ssize_t i = 0; i < Start; )
+ {
+ auto remain = Start - i;
+ auto len = MIN(remain, sizeof(buf));
+ auto rd = Src->Read(buf, len);
+ if (rd != len)
+ {
+ Error = true;
+ break;
+ }
+ i += rd;
+ }
+ }
+ else if (Result != Start)
+ {
Error = true;
+ }
}
+
if (Error)
{
LAssert(!"Failed to set stream position");
}
else
{
- if (len < 0 && Src->GetSize() > 0)
+ if (len >= 0)
+ {
+ Len = len;
+ }
+ else if (Src->GetSize() > 0)
{
Len = Src->GetSize();
if (Start > 0)
Len -= Start;
}
- if (Len >= 0)
+ if (Len > 0)
{
if ((Mem = new char[Alloc = Len]))
{
ssize_t r;
while ( Pos < Len &&
(r = Src->Read(Mem + Pos, Len - Pos)) > 0)
{
Pos += r;
}
Pos = 0;
}
}
else
{
char Buf[512];
LMemQueue p(4 << 10);
ssize_t r;
while ((r = Src->Read(Buf, sizeof(Buf))) > 0)
{
p.Write(Buf, r);
}
Len = Alloc = p.GetSize();
Mem = (char*)p.New(0);
}
}
}
}
LMemStream::LMemStream(const void *mem, int64 len, bool copy)
{
_Init();
Len = len;
if ((Own = copy))
{
if ((Mem = new char[(Alloc = Len)]))
memcpy(Mem, mem, Len);
else
LAssert(!"Alloc error");
}
else
{
Mem = (char*)mem;
}
}
LMemStream::LMemStream(int growBlockSize)
{
_Init();
GrowBlockSize = growBlockSize;
}
LMemStream::~LMemStream()
{
Close();
}
int LMemStream::Open(const char *Str, int Int)
{
LFile f;
if (f.Open(Str, O_READ))
{
Close();
Len = f.GetSize();
Mem = new char[Alloc = Len];
if (Mem)
{
Len = f.Read(Mem, Len);
}
return Len > 0;
}
else LAssert(!"Open error");
return 0;
}
int LMemStream::Close()
{
if (Own)
DeleteArray(Mem);
int Sz = GrowBlockSize;
_Init();
GrowBlockSize = Sz;
return true;
}
int64 LMemStream::SetSize(int64 Size)
{
if (Mem)
{
int64 Common = MIN(Len, Size);
char *NewMem = new char[Alloc = Size];
if (NewMem)
{
memcpy(NewMem, Mem, Common);
DeleteArray(Mem);
Mem = NewMem;
Len = Size;
}
else
{
LAssert(!"Alloc error");
return -1;
}
}
else
{
Close();
Len = Size;
Mem = new char[Alloc = Len];
}
if (Pos > Size)
Pos = Size ? Size - 1 : 0;
return Len;
}
bool LMemStream::IsOk()
{
return Len == 0 || Mem != 0;
}
ssize_t LMemStream::Read(void *Buffer, ssize_t Size, int Flags)
{
ssize_t Bytes = 0;
if (Buffer && Pos >= 0 && Pos < Len)
{
Bytes = MIN(Len - Pos, Size);
memcpy(Buffer, Mem + Pos, Bytes);
Pos += Bytes;
}
return Bytes;
}
ssize_t LMemStream::Write(const void *Buffer, ssize_t Size, int Flags)
{
if (!Buffer || Size <= 0)
return 0;
ssize_t Bytes = 0;
if (GrowBlockSize)
{
// Allow mem stream to grow
char *Ptr = (char*)Buffer;
while (Size)
{
if (Pos >= Alloc)
{
// Grow the mem block
int64 NewSize = Pos + Size;
ssize_t Blocks = (NewSize + GrowBlockSize - 1) / GrowBlockSize;
int64 NewAlloc = Blocks * GrowBlockSize;
char *NewMem = new char[NewAlloc];
if (!NewMem)
return Bytes;
memcpy(NewMem, Mem, Pos);
if (Own)
DeleteArray(Mem);
Mem = NewMem;
Alloc = NewAlloc;
}
if (Pos < Alloc)
{
// Fill available mem first...
ssize_t Remaining = Alloc - Pos;
ssize_t Copy = MIN(Size, Remaining);
memcpy(Mem + Pos, Ptr, Copy);
Size -= Copy;
Ptr += Copy;
Bytes += Copy;
Pos += Copy;
Len = MAX(Pos, Len);
}
else break;
}
}
else if (Pos >= 0 && Pos < Len)
{
// Fill fixed space...
Bytes = MIN(Len - Pos, Size);
memcpy(Mem + Pos, Buffer, Bytes);
Pos += Bytes;
}
return Bytes;
}
LStreamI *LMemStream::Clone()
{
return new LMemStream(Mem, Len, true);
}
ssize_t LMemStream::Write(LStream *Out, ssize_t Size)
{
ssize_t Wr = -1;
if (Out && Size > 0)
{
ssize_t Common = MIN(Size, ((int)(Len-Pos)));
Wr = Out->Write(Mem, Common);
}
return Wr;
}
//////////////////////////////////////////////////////////////////////////////////////////
LTempStream::LTempStream(const char *tmp, int maxMemSize) : LProxyStream(0)
{
s = &Null;
MaxMemSize = maxMemSize;
if (tmp)
TmpFolder = tmp;
else
TmpFolder = LGetSystemPath(LSP_TEMP);
Tmp = 0;
Mem = 0;
}
LTempStream::~LTempStream()
{
Empty();
}
int64 LTempStream::GetSize()
{
if (s == (LStreamI*)&Null)
return 0;
return s->GetSize();
}
void LTempStream::Empty()
{
DeleteObj(Mem);
if (Tmp)
{
char s[MAX_PATH_LEN];
strcpy_s(s, sizeof(s), Tmp->GetName());
DeleteObj(Tmp);
FileDev->Delete(s, false);
}
s = 0;
}
ssize_t LTempStream::Write(const void *Buffer, ssize_t Size, int Flags)
{
if (s == &Null)
{
s = Mem = new LMemStream(16 << 10);
}
ssize_t Status = LProxyStream::Write(Buffer, Size, Flags);
if (Mem != 0 && s->GetSize() > MaxMemSize)
{
// Convert stream from memory to disk as it's getting too large
char c[MAX_PATH_LEN], f[32];
int i=0;
do
{
sprintf_s(f, sizeof(f), "LTempStream-%x.tmp", (int)(((NativeInt)this)+i++));
if (!TmpFolder)
TmpFolder = LGetSystemPath(LSP_TEMP);
if (!TmpFolder)
break;
LMakePath(c, sizeof(c), TmpFolder, f);
}
while (LFileExists(c));
LAssert(Tmp == 0);
if ((Tmp = new LFile))
{
int64 Len = Mem->GetSize();
if (!Tmp->Open(c, O_READWRITE))
{
// Fallback to keeping stuff in memory
DeleteObj(Tmp);
}
else
{
Mem->SetPos(0);
if (Mem->Write(Tmp, Len) != Len)
{
// Copy failed... fall back to mem
DeleteObj(Tmp);
FileDev->Delete(c, false);
}
else
{
// Copy ok...
DeleteObj(Mem);
s = Tmp;
Tmp->SetPos(Tmp->GetSize());
}
}
}
}
return Status;
}
diff --git a/src/win/General/Mem.cpp b/src/win/General/Mem.cpp
--- a/src/win/General/Mem.cpp
+++ b/src/win/General/Mem.cpp
@@ -1,1020 +1,1021 @@
/*hdr
** FILE: Memory.cpp
** AUTHOR: Matthew Allen
** DATE: 10/4/98
** DESCRIPTION: Memory subsystem
**
** Copyright (C) 1998, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include
#include
#include "lgi/common/Mem.h"
+#include "lgi/common/LgiUiBase.h"
#include
#ifdef LGI_MEM_DEBUG
#include "lgi/common/LgiDefs.h"
#include "ImageHlp.h"
#include
#include "LSymLookup.h"
#undef malloc
#undef realloc
#undef free
#define MEM_TRACKING 1
#define MEM_STACK_SIZE 8
#define MEM_FILL 0xcccccccc
#ifndef _WIN64
#define mem_assert(c) if (!(c)) _asm int 3
#else
#define mem_assert LAssert
#endif
struct stack_addr
{
UNativeInt Ip;
};
struct block
{
block *Next, *Prev;
uint32 Size;
stack_addr Stack[MEM_STACK_SIZE];
uint32 PreBytes;
};
static bool DoLeakCheck = true;
static bool MemInit = false;
static uint BlockCount = 0;
static block *First = 0;
static block *Last = 0;
static CRITICAL_SECTION Lock;
void LSetLeakDetect(bool On)
{
DoLeakCheck = On;
}
#pragma warning(disable:4074) // warning C4074: initializers put in compiler reserved initialization area
#pragma init_seg(compiler)
struct LLeakCheck
{
LLeakCheck()
{
InitializeCriticalSection(&Lock);
MemInit = true;
}
~LLeakCheck()
{
char DefName[] = "leaks.mem";
if (First && DoLeakCheck)
{
LDumpMemoryStats(DefName);
}
else
{
unlink(DefName);
}
DeleteCriticalSection(&Lock);
MemInit = false;
}
} _LeakCheck;
typedef struct _CALL_FRAME
{
struct _CALL_FRAME* Next;
void* ReturnAddress;
} CALL_FRAME, * PCALL_FRAME;
LONG
StackwalkExceptionHandler(PEXCEPTION_POINTERS ExceptionPointers)
{
if (ExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_EXECUTE_HANDLER;
return EXCEPTION_CONTINUE_SEARCH;
}
void *lgi_malloc(size_t size)
{
#if MEM_TRACKING
LAssert(MemInit);
if (size > 0)
{
void *Ret = 0;
EnterCriticalSection(&Lock);
int total = sizeof(block) + sizeof(int32) + size;
block *b = (block*) malloc(total);
if (b)
{
// Init the structure
b->Size = size;
b->PreBytes = MEM_FILL;
memset(b->Stack, 0, sizeof(b->Stack));
// Save the stack trace
void *_ebp = 0;
#ifndef _WIN64
_asm {
mov eax, ebp
mov _ebp, eax
}
#if 1
PCALL_FRAME frame = (PCALL_FRAME)_ebp;
__try
{
for (int i=0; i<=MEM_STACK_SIZE; i++)
{
if (!frame || (NativeInt)frame < 0x1000 || (*((unsigned*)frame) & 0x3))
break;
if (i)
b->Stack[i-1].Ip = (UNativeInt) *((uint8_t**)frame + 1);
frame = frame->Next;
}
}
__except(StackwalkExceptionHandler(GetExceptionInformation()))
{
}
#else
for (int i=-1; i= 0)
b->Stack[i].Ip = (uint) *((uint8_t**)RegEbp + 1);
RegEbp = *(uint*)RegEbp;
}
#endif
#else
USHORT r = CaptureStackBackTrace(2, MEM_STACK_SIZE, (PVOID*)b->Stack, NULL);
#endif
// Add to linked list
if (Last)
{
Last->Next = b;
b->Prev = Last;
Last = b;
}
else
{
First = Last = b;
b->Prev = 0;
}
b->Next = 0;
BlockCount++;
// Return the data area
Ret = (b + 1);
}
LeaveCriticalSection(&Lock);
return Ret;
}
#else
if (size > 0)
return malloc(size);
#endif
return 0;
}
void *lgi_realloc(void *ptr, size_t size)
{
#if MEM_TRACKING
if (ptr)
{
void *ret = 0;
EnterCriticalSection(&Lock);
block *b = ((block*)ptr) - 1;
if (b)
{
block *prev = b->Prev;
block *next = b->Next;
int total = sizeof(block) + sizeof(int32) + size;
block *n = (block*)realloc(b, total);
if (n)
{
n->Size = size;
if (prev)
prev->Next = n;
else
First = n;
LAssert(n->Prev == prev);
if (next)
next->Prev = n;
else
Last = n;
LAssert(n->Next == next);
ret = n + 1;
}
}
LeaveCriticalSection(&Lock);
return ret;
}
#endif
return 0;
}
void lgi_free(void *ptr)
{
#if MEM_TRACKING
if (ptr)
{
EnterCriticalSection(&Lock);
block *b = ((block*)ptr) - 1;
if (b)
{
// Check struct
if (b->PreBytes != MEM_FILL)
{
mem_assert(!"Pre-fill bytes wrong");
}
// Remove from list
if (b == First)
{
mem_assert(b->Prev == 0);
if (b->Next)
{
b->Next->Prev = 0;
}
First = b->Next;
}
else if (b == Last)
{
mem_assert(b->Next == 0);
if (b->Prev)
{
b->Prev->Next = 0;
}
Last = b->Prev;
}
else
{
mem_assert(b->Next != 0);
if (b->Next)
{
b->Next->Prev = b->Prev;
}
mem_assert(b->Prev != 0);
if (b->Prev)
{
b->Prev->Next = b->Next;
}
}
BlockCount--;
// Free the memory
free(b);
}
LeaveCriticalSection(&Lock);
}
#else
if (ptr)
free(ptr);
#endif
}
typedef BOOL (__stdcall *SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL );
typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE );
typedef BOOL (__stdcall * STACKWALKPROC)
( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
PREAD_PROCESS_MEMORY_ROUTINE,PFUNCTION_TABLE_ACCESS_ROUTINE,
PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE );
typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD );
typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD );
typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)
( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL );
typedef BOOL (__stdcall *proc_SymGetLineFromAddr)( HANDLE hProcess,
DWORD dwAddr,
PDWORD pdwDisplacement,
PIMAGEHLP_LINE Line);
typedef DWORD (__stdcall *proc_SymGetOptions)(VOID);
typedef DWORD (__stdcall *proc_SymSetOptions)(DWORD SymOptions);
bool LDumpMemoryStats(char *filename)
{
bool Status = true;
HMODULE DbgHelp = LoadLibraryA("dbghelp.dll");
if (DbgHelp)
{
HANDLE hProcess = GetCurrentProcess();
SYMINITIALIZEPROC SymInitialize;
SYMCLEANUPPROC SymCleanup;
STACKWALKPROC StackWalk;
SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess;
SYMGETMODULEBASEPROC SymGetModuleBase;
#ifdef _WIN64
pSymFromAddr SymFromAddr;
pSymGetLineFromAddr64 SymGetLineFromAddr64;
#else
SYMGETSYMFROMADDRPROC SymGetSymFromAddr;
proc_SymGetLineFromAddr SymGetLineFromAddr;
#endif
proc_SymGetOptions SymGetOptions;
proc_SymSetOptions SymSetOptions;
SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(DbgHelp, "SymInitialize");
SymCleanup = (SYMCLEANUPPROC) GetProcAddress(DbgHelp, "SymCleanup");
StackWalk = (STACKWALKPROC) GetProcAddress(DbgHelp, "StackWalk");
SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(DbgHelp, "SymFunctionTableAccess");
SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(DbgHelp, "SymGetModuleBase");
#ifdef _WIN64
SymFromAddr = (pSymFromAddr) GetProcAddress(DbgHelp, "SymFromAddr");
SymGetLineFromAddr64 = (pSymGetLineFromAddr64) GetProcAddress(DbgHelp, "SymGetLineFromAddr64");
#else
SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(DbgHelp, "SymGetSymFromAddr");
SymGetLineFromAddr = (proc_SymGetLineFromAddr) GetProcAddress(DbgHelp, "SymGetLineFromAddr");
#endif
SymGetOptions = (proc_SymGetOptions) GetProcAddress(DbgHelp, "SymGetOptions");
SymSetOptions = (proc_SymSetOptions) GetProcAddress(DbgHelp, "SymSetOptions");
DWORD dwOpts = SymGetOptions();
DWORD Set = SymSetOptions(dwOpts | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST);
char s[512] = "";
GetModuleFileNameA(NULL, s, sizeof(s));
char *end = strrchr(s, '\\');
if (end)
{
*end = 0;
}
char *path = getenv("path");
int pathlen = strlen(path);
char *all = (char*)malloc(pathlen + strlen(s) + 256);
if (all)
{
strcpy(all, path);
if (all[strlen(all)-1] != ';')
strcat(all, ";");
strcat(all, s);
if (SymInitialize(hProcess, all, true))
{
char FullPath[MAX_PATH_LEN];
_getcwd(FullPath, sizeof(FullPath));
strcat(FullPath, "\\");
strcat(FullPath, filename ? filename : "stats.mem");
FILE *f = fopen(FullPath, "w");
if (f)
{
block *b;
uint TotalBytes = 0;
for (b = First; b; b = b->Next)
{
TotalBytes += b->Size;
}
sprintf_s(s, sizeof(s), "%i bytes (%.1f MB) in %i blocks:\n%s\n",
TotalBytes, (double)TotalBytes / 1024 / 1024, BlockCount,
FullPath);
fwrite(s, 1, strlen(s), f);
OutputDebugStringA(s);
int64 Start = GetTickCount();
int64 Last = Start;
uint Current = 0;
for (b = First; b; b = b->Next, Current++)
{
int64 Now = GetTickCount();
if (Now > Last + 1000)
{
Last = Now;
double Sec = (double)(Now-Start)/1000;
sprintf_s( s, sizeof(s),
"Dumping %.1f%%, %i of %i (%i/sec)\n",
(double)Current * 100 / BlockCount,
Current,
BlockCount,
(int)(Current/Sec));
OutputDebugStringA(s);
}
uint8_t *Data = (uint8_t*)(b + 1);
int ch = sprintf_s(s, sizeof(s), "Block %p, %i bytes = {", b + 1, b->Size);
for (int n=0; nSize && n<16; n++)
{
if (Data[n] >= ' ' && Data[n] < 127)
{
ch += sprintf_s(s+ch, sizeof(s)-ch, "'%c', ", Data[n]);
}
else
{
ch += sprintf_s(s+ch, sizeof(s)-ch, "0x%02.2x, ", (int)Data[n]);
}
}
if (b->Size > 16)
{
ch += sprintf_s(s+ch, sizeof(s)-ch, "...}\n");
}
else
{
ch += sprintf_s(s+ch, sizeof(s)-ch, "}\n");
}
fwrite(s, 1, ch, f);
for (int i=0; b->Stack[i].Ip && iStack[i].Ip, &mbi, sizeof(mbi));
HINSTANCE Pid = (HINSTANCE)mbi.AllocationBase;
char module[256] = "";
if (GetModuleFileNameA(Pid, module, sizeof(module)))
{
DWORD Offset = b->Stack[i].Ip - (UNativeInt)Pid;
#ifdef _WIN64
BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 512 ];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
IMAGEHLP_LINE64 Line;
ZeroObj(symbolBuffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = 512;
#else
BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 512 ];
PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
IMAGEHLP_LINE Line;
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLength = 512;
#endif
memset(&Line, 0, sizeof(Line));
Line.SizeOfStruct = sizeof(Line);
char *mod = strrchr(module, '\\');
if (mod) mod++;
else mod = module;
#ifdef _WIN64
DWORD64 symDisplacement = 0;
if (SymFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, pSymbol))
{
DWORD Displacement = symDisplacement;
if (SymGetLineFromAddr64(hProcess, b->Stack[i].Ip, &Displacement, &Line))
sprintf_s(s, sizeof(s), "\t%s %s:%i\n", mod, Line.FileName, Line.LineNumber);
else
{
if (pSymbol->Name[0] == '$')
break;
sprintf_s(s, sizeof(s), "\t%s %s+0x" LPrintfHex64 "\n", mod, pSymbol->Name, symDisplacement);
// dumpBuffer.Printf("%p: %s Offset: 0x%X (%hs)\n", caller, module, symDisplacement, pSymbol->Name);
}
}
else
sprintf_s(s, sizeof(s), "\t%s 0x" LPrintfHex64 "\n", mod, b->Stack[i].Ip);
#else
DWORD symDisplacement = 0;
if (SymGetSymFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, pSymbol))
{
if (SymGetLineFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, &Line))
sprintf_s(s, sizeof(s), "\t%s %s:%i\n", mod, Line.FileName, Line.LineNumber);
else
{
if (pSymbol->Name[0] == '$')
break;
sprintf_s(s, sizeof(s), "\t%s %s+0x%x\n", mod, pSymbol->Name, symDisplacement);
// dumpBuffer.Printf("%p: %s Offset: 0x%X (%hs)\n", caller, module, symDisplacement, pSymbol->Name);
}
}
else
sprintf_s(s, sizeof(s), "\t%s 0x%x\n", mod, b->Stack[i].Ip);
#endif
fwrite(s, 1, strlen(s), f);
}
}
fwrite("\n", 1, 1, f);
}
fclose(f);
}
else Status = false;
SymCleanup(hProcess);
}
else Status = false;
}
FreeLibrary(DbgHelp);
}
else Status = false;
return Status;
}
void *operator new(size_t size)
{
return lgi_malloc(size);
}
void *operator new[](size_t size)
{
return lgi_malloc(size);
}
void operator delete(void *p)
{
lgi_free(p);
}
void operator delete[](void *p)
{
lgi_free(p);
}
#else
#include
#define nNoMansLandSize 4
typedef struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
#ifdef _WIN64
/* These items are reversed on Win64 to eliminate gaps in the struct
* and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
* maintained in the debug heap.
*/
int nBlockUse;
size_t nDataSize;
#else /* _WIN64 */
size_t nDataSize;
int nBlockUse;
#endif /* _WIN64 */
long lRequest;
unsigned char gap[nNoMansLandSize];
/* followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];
*/
} _CrtMemBlockHeader;
bool LDumpMemoryStats(char *filename)
{
#ifdef _DEBUG
_CrtMemState state = {0};
_CrtMemCheckpoint(&state);
_CrtMemDumpStatistics(&state);
FILE *f;
#ifdef _MSC_VER
auto ret = fopen_s(&f,
#else
f = fopen(
#endif
filename ? filename : "memdump.txt", "w");
if (!f)
return false;
char buf[512];
int i = 0;
for (auto blk = state.pBlockHeader; blk; blk = blk->pBlockHeaderNext)
{
int ch = sprintf_s( buf, sizeof(buf),
"%i,%s:%i,%i," LPrintfSizeT ",%li\n",
i++,
blk->szFileName,
blk->nLine,
blk->nBlockUse,
blk->nDataSize,
blk->lRequest);
fwrite(buf, 1, ch, f);
if (i % 10000 == 0)
LgiTrace("Dumping %i..\n", i);
}
fclose(f);
return true;
#else
return false;
#endif
}
void LSetLeakDetect(bool On)
{
}
#endif // LGI_MEM_DEBUG
/*
#ifdef MEMORY_DEBUG
#if 0
#define _malloc(s) VirtualAlloc(0, s, MEM_COMMIT, PAGE_READWRITE)
#define _free(p) VirtualFree(p, 0, MEM_RELEASE);
#else
#include
#define abs(a) ((a)>0 ? (a) : -(a))
#define _malloc(s) malloc(s)
#define _free(p) free(p);
#endif
char *_vmem_file = 0;
int _vmem_line = 0;
#include
#define _VMEM_MAGIC 0x12345678
#define _VMEM_BUFFER 16
#define _VMEM_FILL 0xcdcdcdcd
#ifdef _METRICS
class _vmem_metrics
{
class _metric
{
public:
char *File;
int Line;
int Count;
_metric()
{
File = 0;
Line = 0;
Count = 0;
}
};
int Length;
_metric *m;
LMutex Lock;
public:
_vmem_metrics()
{
Length = 16 << 10;
int Bytes = sizeof(_metric) * Length;
m = (_metric*) malloc(Bytes);
if (m)
{
memset(m, 0, Bytes);
}
}
~_vmem_metrics()
{
for (int i=0; i<10; i++)
{
_metric *Max = m;
for (int n=0; n Max->Count)
{
Max = m + n;
}
}
if (Max->Count)
{
char s[256];
sprintf_s(s, sizeof(s), "Metrics: %s,%i Count: %i\n", Max->File, Max->Line, Max->Count);
OutputDebugString(s);
Max->Count = 0;
}
}
free(m);
}
void OnAlloc(char *file, int line)
{
if (Lock.Lock())
{
int i;
for (i=0; i= ' ')
*e++ = Data[i];
else
*e++ = '.';
}
strcpy(e, "')\n");
OutputDebugString(s);
}
};
int _used = 0;
int _max_used = 0;
_vmem_info *_First, *_Last;
class _VMemCleanup
{
public:
_VMemCleanup()
{
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_WNDW);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
}
~_VMemCleanup()
{
LAssert(LCheckHeap());
char s[256];
if (_First)
{
sprintf_s(s, sizeof(s), "Memory Leaks: %i (Max blocks %i)\n", _used, _max_used);
OutputDebugString(s);
int k=0;
for (_vmem_info *i=_First; i; k++)
{
i->Dump();
void *p = i;
i = i->Next;
_free(p);
}
}
else
{
sprintf_s(s, sizeof(s), "No Memory Leaks. (Max blocks %i)\n", _max_used);
OutputDebugString(s);
}
}
} VMemCleanup;
int _Allocated = 0;
void *_vmem_alloc(size_t Size, char *file, int line)
{
int TotalSize = Size + (_VMEM_BUFFER * 2);
_vmem_info *p = (_vmem_info*)_malloc(sizeof(_vmem_info) + TotalSize);
if (p)
{
#ifdef _METRICS
VmemMetrics.OnAlloc(file, line);
#endif
// set the members
p->Magic = _VMEM_MAGIC;
p->File = file;
p->Line = line;
p->Size = Size;
_used++;
_max_used = max(_used, _max_used);
// insert into the list
if (!_First)
{
_First = _Last = p;
p->Prev = p->Next = 0;
}
else
{
p->Prev = 0;
p->Next = _First;
_First->Prev = p;
_First = p;
}
return ((uchar*)(p+1)) + _VMEM_BUFFER;
}
else
{
// out of memory
_asm int 3
}
return 0;
}
void _vmem_free(void *ptr)
{
if (ptr)
{
_vmem_info *p = ((_vmem_info*) ( ((uchar*)ptr) - _VMEM_BUFFER) ) - 1;
if (p->Magic == _VMEM_MAGIC)
{
// Check fill
if (!p->CheckFill())
{
_asm int 3
}
// Remove from the list
if (p == _First && p == _Last)
{
_First = _Last = 0;
}
else if (p == _First)
{
if (p->Prev) _asm int 3
_First = p->Next;
if (_First) _First->Prev = 0;
}
else if (p == _Last)
{
if (p->Next) _asm int 3
_Last = p->Prev;
if (p->Prev) p->Prev->Next = 0;
}
else
{
if (!p->Next || !p->Prev) _asm int 3
p->Next->Prev = p->Prev;
p->Prev->Next = p->Next;
}
p->Prev = p->Next = 0;
_used--;
_free(p);
}
else
{
// this pointer wasn't allocated by us
_asm int 3
}
}
else
{
// no pointer
_asm int 3
}
}
#endif
*/
bool LCheckHeap()
{
#ifdef MEMORY_DEBUG
_vmem_info *i = 0;
int n = 0;
int MinD = 0x7fffffff;
try
{
for (i=_First; i; i=i->Next, n++)
{
char c = i->File[0];
if (i->Magic != _VMEM_MAGIC ||
!i->CheckFill())
{
_asm int 3
}
}
}
catch (...)
{
_asm int 3
return false;
}
#endif
#if 0 // defined _MSC_VER && defined _CRT_USE_WINAPI_FAMILY_DESKTOP_APP
if (_heapchk() != _HEAPOK)
{
assert(!"Heap not ok.");
return false;
}
#endif
return true;
}
bool LgiCanReadMemory(void *p, int Len)
{
/*
try
{
int i = 0;
for (char *Ptr = (char*)p; Len > 0; Len--)
{
i += *Ptr++;
}
}
catch (...)
{
// I guess not... then..
return false;
}
*/
return true;
}