Added: Win64 compatibility
Fixed: XML persist reader creating sections Fixed: new MIME implementation for XMLDataBindingUtils
This commit is contained in:
parent
4d9a76cb35
commit
a212dbd74c
@ -9,8 +9,8 @@
|
|||||||
<ProjectVersion>13.4</ProjectVersion>
|
<ProjectVersion>13.4</ProjectVersion>
|
||||||
<Base>True</Base>
|
<Base>True</Base>
|
||||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||||
<Platform Condition="'$(Platform)'==''">Win32</Platform>
|
<Platform Condition="'$(Platform)'==''">Win64</Platform>
|
||||||
<TargetedPlatforms>1</TargetedPlatforms>
|
<TargetedPlatforms>3</TargetedPlatforms>
|
||||||
<AppType>Package</AppType>
|
<AppType>Package</AppType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||||
@ -31,11 +31,29 @@
|
|||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
<Base>true</Base>
|
<Base>true</Base>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win64)'!=''">
|
||||||
|
<Cfg_1_Win64>true</Cfg_1_Win64>
|
||||||
|
<CfgParent>Cfg_1</CfgParent>
|
||||||
|
<Cfg_1>true</Cfg_1>
|
||||||
|
<Base>true</Base>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
|
||||||
|
<Cfg_1_Win32>true</Cfg_1_Win32>
|
||||||
|
<CfgParent>Cfg_1</CfgParent>
|
||||||
|
<Cfg_1>true</Cfg_1>
|
||||||
|
<Base>true</Base>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
||||||
<Cfg_2>true</Cfg_2>
|
<Cfg_2>true</Cfg_2>
|
||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
<Base>true</Base>
|
<Base>true</Base>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win64)'!=''">
|
||||||
|
<Cfg_2_Win64>true</Cfg_2_Win64>
|
||||||
|
<CfgParent>Cfg_2</CfgParent>
|
||||||
|
<Cfg_2>true</Cfg_2>
|
||||||
|
<Base>true</Base>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
|
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
|
||||||
<Cfg_2_Win32>true</Cfg_2_Win32>
|
<Cfg_2_Win32>true</Cfg_2_Win32>
|
||||||
<CfgParent>Cfg_2</CfgParent>
|
<CfgParent>Cfg_2</CfgParent>
|
||||||
@ -57,14 +75,20 @@
|
|||||||
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
|
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Base_Win64)'!=''">
|
<PropertyGroup Condition="'$(Base_Win64)'!=''">
|
||||||
|
<DCC_Namespace>Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)</DCC_Namespace>
|
||||||
|
<DCC_BplOutput>$(DELPHIBIN64)</DCC_BplOutput>
|
||||||
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
|
<DCC_DcpOutput>$(DELPHIBIN64)</DCC_DcpOutput>
|
||||||
|
<DCC_DcuOutput>$(DELPHILIB64)</DCC_DcuOutput>
|
||||||
<Icon_MainIcon>X2Utils_Icon.ico</Icon_MainIcon>
|
<Icon_MainIcon>X2Utils_Icon.ico</Icon_MainIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||||
|
<DCC_BplOutput>$(DELPHIBIN)</DCC_BplOutput>
|
||||||
|
<DCC_DcuOutput>$(DELPHILIB)</DCC_DcuOutput>
|
||||||
|
<DCC_DcpOutput>$(DELPHIBIN)</DCC_DcpOutput>
|
||||||
<Icon_MainIcon>X2Utils_Icon.ico</Icon_MainIcon>
|
<Icon_MainIcon>X2Utils_Icon.ico</Icon_MainIcon>
|
||||||
<DCC_Namespace>Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
<DCC_Namespace>Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
|
||||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
||||||
<Version>7.0</Version>
|
<Version>7.0</Version>
|
||||||
@ -75,15 +99,21 @@
|
|||||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
||||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
|
||||||
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
|
||||||
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||||
<Version>7.0</Version>
|
<Version>7.0</Version>
|
||||||
<DCC_WriteableConstants>True</DCC_WriteableConstants>
|
<DCC_WriteableConstants>True</DCC_WriteableConstants>
|
||||||
<DCC_GenerateStackFrames>True</DCC_GenerateStackFrames>
|
<DCC_GenerateStackFrames>True</DCC_GenerateStackFrames>
|
||||||
<DCC_DcuOutput>$(DELPHILIB)</DCC_DcuOutput>
|
|
||||||
<DCC_ObjOutput>$(DELPHILIB)</DCC_ObjOutput>
|
<DCC_ObjOutput>$(DELPHILIB)</DCC_ObjOutput>
|
||||||
<DCC_HppOutput>$(DELPHILIB)</DCC_HppOutput>
|
<DCC_HppOutput>$(DELPHILIB)</DCC_HppOutput>
|
||||||
<DCC_BplOutput>$(DELPHIBIN)</DCC_BplOutput>
|
</PropertyGroup>
|
||||||
<DCC_DcpOutput>$(DELPHIBIN)</DCC_DcpOutput>
|
<PropertyGroup Condition="'$(Cfg_2_Win64)'!=''">
|
||||||
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
|
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
|
||||||
<DllSuffix>XE2</DllSuffix>
|
<DllSuffix>XE2</DllSuffix>
|
||||||
@ -135,7 +165,7 @@
|
|||||||
</Excluded_Packages>
|
</Excluded_Packages>
|
||||||
</Delphi.Personality>
|
</Delphi.Personality>
|
||||||
<Platforms>
|
<Platforms>
|
||||||
<Platform value="Win64">False</Platform>
|
<Platform value="Win64">True</Platform>
|
||||||
<Platform value="Win32">True</Platform>
|
<Platform value="Win32">True</Platform>
|
||||||
</Platforms>
|
</Platforms>
|
||||||
</BorlandProject>
|
</BorlandProject>
|
||||||
|
Binary file not shown.
@ -35,6 +35,7 @@ type
|
|||||||
private
|
private
|
||||||
FFileName: String;
|
FFileName: String;
|
||||||
FConfiguration: IXMLConfiguration;
|
FConfiguration: IXMLConfiguration;
|
||||||
|
FIsReader: Boolean;
|
||||||
FSection: IXMLSection;
|
FSection: IXMLSection;
|
||||||
FSectionStack: TInterfaceList;
|
FSectionStack: TInterfaceList;
|
||||||
protected
|
protected
|
||||||
@ -71,6 +72,7 @@ type
|
|||||||
|
|
||||||
|
|
||||||
property Configuration: IXMLConfiguration read FConfiguration;
|
property Configuration: IXMLConfiguration read FConfiguration;
|
||||||
|
property IsReader: Boolean read FIsReader;
|
||||||
property Section: IXMLSection read FSection;
|
property Section: IXMLSection read FSection;
|
||||||
property SectionStack: TInterfaceList read FSectionStack;
|
property SectionStack: TInterfaceList read FSectionStack;
|
||||||
property FileName: String read FFileName;
|
property FileName: String read FFileName;
|
||||||
@ -132,6 +134,7 @@ begin
|
|||||||
|
|
||||||
FSectionStack := TInterfaceList.Create;
|
FSectionStack := TInterfaceList.Create;
|
||||||
FFileName := AFileName;
|
FFileName := AFileName;
|
||||||
|
FIsReader := AIsReader;
|
||||||
|
|
||||||
if AIsReader then
|
if AIsReader then
|
||||||
FConfiguration := LoadConfiguration(AFileName)
|
FConfiguration := LoadConfiguration(AFileName)
|
||||||
@ -170,7 +173,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if not Result then
|
if (not Result) and (not IsReader) then
|
||||||
begin
|
begin
|
||||||
FSection := Section.section.Add;
|
FSection := Section.section.Add;
|
||||||
FSection.name := AName;
|
FSection.name := AName;
|
||||||
|
@ -148,12 +148,6 @@ type
|
|||||||
function MimeDecodeString(const S: AnsiString): AnsiString; forward;
|
function MimeDecodeString(const S: AnsiString): AnsiString; forward;
|
||||||
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream); forward;
|
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream); forward;
|
||||||
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream); forward;
|
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream); forward;
|
||||||
function MimeEncodedSize(const I: Cardinal): Cardinal; forward;
|
|
||||||
function MimeDecodedSize(const I: Cardinal): Cardinal; forward;
|
|
||||||
procedure MimeEncode(var InputBuffer; const InputByteCount: Cardinal; var OutputBuffer); forward;
|
|
||||||
function MimeDecode(var InputBuffer; const InputBytesCount: Cardinal; var OutputBuffer): Cardinal; forward;
|
|
||||||
function MimeDecodePartial(var InputBuffer; const InputBytesCount: Cardinal; var OutputBuffer; var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal; forward;
|
|
||||||
function MimeDecodePartialEnd(var OutputBuffer; const ByteBuffer: Cardinal; const ByteBufferSpace: Cardinal): Cardinal; forward;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -590,45 +584,65 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
{ --- JclMime implementation from here. }
|
{ --- JclMime implementation from here. }
|
||||||
// Caution: For MimeEncodeStream and all other kinds of multi-buffered
|
type
|
||||||
// Mime encodings (i.e. Files etc.), BufferSize must be set to a multiple of 3.
|
{$IFDEF WIN64}
|
||||||
// Even though the implementation of the Mime decoding routines below
|
SizeInt = NativeInt;
|
||||||
// do not require a particular buffer size, they work fastest with sizes of
|
TJclAddr = UInt64;
|
||||||
// multiples of four. The chosen size is a multiple of 3 and of 4 as well.
|
{$ELSE}
|
||||||
// The following numbers are, in addition, also divisible by 1024:
|
SizeInt = Integer;
|
||||||
// $2400, $3000, $3C00, $4800, $5400, $6000, $6C00.
|
TJclAddr = Cardinal;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
PByte4 = ^TByte4;
|
||||||
|
TByte4 = packed record
|
||||||
|
B1: Byte;
|
||||||
|
B2: Byte;
|
||||||
|
B3: Byte;
|
||||||
|
B4: Byte;
|
||||||
|
end;
|
||||||
|
|
||||||
|
PByte3 = ^TByte3;
|
||||||
|
TByte3 = packed record
|
||||||
|
B1: Byte;
|
||||||
|
B2: Byte;
|
||||||
|
B3: Byte;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
BUFFER_SIZE = $3000;
|
MIME_ENCODED_LINE_BREAK = 76;
|
||||||
EqualSign = Byte('=');
|
MIME_DECODED_LINE_BREAK = MIME_ENCODED_LINE_BREAK div 4 * 3;
|
||||||
|
MIME_BUFFER_SIZE = MIME_DECODED_LINE_BREAK * 3 * 4 * 4;
|
||||||
|
|
||||||
MIME_ENCODE_TABLE: array [0..63] of Byte = (
|
MIME_ENCODE_TABLE: array [0..63] of Byte = (
|
||||||
65, 66, 67, 68, 69, 70, 71, 72, // 00 - 07
|
065, 066, 067, 068, 069, 070, 071, 072, // 00 - 07
|
||||||
73, 74, 75, 76, 77, 78, 79, 80, // 08 - 15
|
073, 074, 075, 076, 077, 078, 079, 080, // 08 - 15
|
||||||
81, 82, 83, 84, 85, 86, 87, 88, // 16 - 23
|
081, 082, 083, 084, 085, 086, 087, 088, // 16 - 23
|
||||||
89, 90, 97, 98, 99, 100, 101, 102, // 24 - 31
|
089, 090, 097, 098, 099, 100, 101, 102, // 24 - 31
|
||||||
103, 104, 105, 106, 107, 108, 109, 110, // 32 - 39
|
103, 104, 105, 106, 107, 108, 109, 110, // 32 - 39
|
||||||
111, 112, 113, 114, 115, 116, 117, 118, // 40 - 47
|
111, 112, 113, 114, 115, 116, 117, 118, // 40 - 47
|
||||||
119, 120, 121, 122, 48, 49, 50, 51, // 48 - 55
|
119, 120, 121, 122, 048, 049, 050, 051, // 48 - 55
|
||||||
52, 53, 54, 55, 56, 57, 43, 47); // 56 - 63
|
052, 053, 054, 055, 056, 057, 043, 047); // 56 - 63
|
||||||
|
|
||||||
MIME_DECODE_TABLE: array [Byte] of Cardinal = (
|
MIME_PAD_CHAR = Byte('=');
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, // 00 - 07
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, // 08 - 15
|
MIME_DECODE_TABLE: array [Byte] of Byte = (
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, // 0 - 7
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, // 8 - 15
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, // 16 - 23
|
255, 255, 255, 255, 255, 255, 255, 255, // 16 - 23
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, // 24 - 31
|
255, 255, 255, 255, 255, 255, 255, 255, // 24 - 31
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, // 32 - 39
|
255, 255, 255, 255, 255, 255, 255, 255, // 32 - 39
|
||||||
255, 255, 255, 62, 255, 255, 255, 63, // 40 - 47
|
255, 255, 255, 062, 255, 255, 255, 063, // 40 - 47
|
||||||
52, 53, 54, 55, 56, 57, 58, 59, // 48 - 55
|
052, 053, 054, 055, 056, 057, 058, 059, // 48 - 55
|
||||||
60, 61, 255, 255, 255, 255, 255, 255, // 56 - 63
|
060, 061, 255, 255, 255, 255, 255, 255, // 56 - 63
|
||||||
255, 0, 1, 2, 3, 4, 5, 6, // 64 - 71
|
255, 000, 001, 002, 003, 004, 005, 006, // 64 - 71
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, // 72 - 79
|
007, 008, 009, 010, 011, 012, 013, 014, // 72 - 79
|
||||||
15, 16, 17, 18, 19, 20, 21, 22, // 80 - 87
|
015, 016, 017, 018, 019, 020, 021, 022, // 80 - 87
|
||||||
23, 24, 25, 255, 255, 255, 255, 255, // 88 - 95
|
023, 024, 025, 255, 255, 255, 255, 255, // 88 - 95
|
||||||
255, 26, 27, 28, 29, 30, 31, 32, // 96 - 103
|
255, 026, 027, 028, 029, 030, 031, 032, // 96 - 103
|
||||||
33, 34, 35, 36, 37, 38, 39, 40, // 104 - 111
|
033, 034, 035, 036, 037, 038, 039, 040, // 104 - 111
|
||||||
41, 42, 43, 44, 45, 46, 47, 48, // 112 - 119
|
041, 042, 043, 044, 045, 046, 047, 048, // 112 - 119
|
||||||
49, 50, 51, 255, 255, 255, 255, 255, // 120 - 127
|
049, 050, 051, 255, 255, 255, 255, 255, // 120 - 127
|
||||||
255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
@ -646,259 +660,218 @@ const
|
|||||||
255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255);
|
255, 255, 255, 255, 255, 255, 255, 255);
|
||||||
|
|
||||||
type
|
|
||||||
PByte4 = ^TByte4;
|
|
||||||
TByte4 = packed record
|
|
||||||
B1: Byte;
|
|
||||||
B2: Byte;
|
|
||||||
B3: Byte;
|
|
||||||
B4: Byte;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PByte3 = ^TByte3;
|
function MimeEncodedSize(const InputSize: SizeInt): SizeInt;
|
||||||
TByte3 = packed record
|
|
||||||
B1: Byte;
|
|
||||||
B2: Byte;
|
|
||||||
B3: Byte;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Wrapper functions & procedures
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeEncodeString(const S: AnsiString): AnsiString;
|
|
||||||
var
|
|
||||||
L: Cardinal;
|
|
||||||
begin
|
begin
|
||||||
L := Length(S);
|
if InputSize > 0 then
|
||||||
if L > 0 then
|
Result := (InputSize + 2) div 3 * 4 + (InputSize - 1) div MIME_DECODED_LINE_BREAK * 2
|
||||||
begin
|
|
||||||
SetLength(Result, MimeEncodedSize(L));
|
|
||||||
MimeEncode(PAnsiChar(S)^, L, PAnsiChar(Result)^);
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
Result := '';
|
Result := InputSize;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeDecodeString(const S: AnsiString): AnsiString;
|
procedure MimeEncodeFullLines(const InputBuffer; const InputByteCount: SizeInt; out OutputBuffer);
|
||||||
var
|
|
||||||
ByteBuffer, ByteBufferSpace: Cardinal;
|
|
||||||
L: Cardinal;
|
|
||||||
begin
|
|
||||||
L := Length(S);
|
|
||||||
if L > 0 then
|
|
||||||
begin
|
|
||||||
SetLength(Result, MimeDecodedSize(L));
|
|
||||||
ByteBuffer := 0;
|
|
||||||
ByteBufferSpace := 4;
|
|
||||||
L := MimeDecodePartial(PAnsiChar(S)^, L, PAnsiChar(Result)^, ByteBuffer, ByteBufferSpace);
|
|
||||||
Inc(L, MimeDecodePartialEnd(PAnsiChar(Cardinal(Result) + L)^, ByteBuffer, ByteBufferSpace));
|
|
||||||
SetLength(Result, L);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
||||||
var
|
|
||||||
InputBuffer: array [0..BUFFER_SIZE - 1] of Byte;
|
|
||||||
OutputBuffer: array [0..((BUFFER_SIZE + 2) div 3) * 4 - 1] of Byte;
|
|
||||||
BytesRead: Integer;
|
|
||||||
begin
|
|
||||||
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
||||||
while BytesRead > 0 do
|
|
||||||
begin
|
|
||||||
MimeEncode(InputBuffer, BytesRead, OutputBuffer);
|
|
||||||
OutputStream.Write(OutputBuffer, MimeEncodedSize(BytesRead));
|
|
||||||
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
||||||
var
|
|
||||||
ByteBuffer, ByteBufferSpace: Cardinal;
|
|
||||||
InputBuffer: array [0..(BUFFER_SIZE + 3) div 4 * 3 - 1] of Byte;
|
|
||||||
OutputBuffer: array [0..BUFFER_SIZE - 1] of Byte;
|
|
||||||
BytesRead: Integer;
|
|
||||||
begin
|
|
||||||
ByteBuffer := 0;
|
|
||||||
ByteBufferSpace := 4;
|
|
||||||
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
||||||
while BytesRead > 0 do
|
|
||||||
begin
|
|
||||||
OutputStream.Write(OutputBuffer, MimeDecodePartial(InputBuffer, BytesRead, OutputBuffer, ByteBuffer, ByteBufferSpace));
|
|
||||||
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
||||||
end;
|
|
||||||
OutputStream.Write(OutputBuffer, MimeDecodePartialEnd(OutputBuffer, ByteBuffer, ByteBufferSpace));
|
|
||||||
end;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Helper functions
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeEncodedSize(const I: Cardinal): Cardinal;
|
|
||||||
begin
|
|
||||||
Result := (I + 2) div 3 * 4;
|
|
||||||
end;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeDecodedSize(const I: Cardinal): Cardinal;
|
|
||||||
begin
|
|
||||||
Result := (I + 3) div 4 * 3;
|
|
||||||
end;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Primary functions & procedures
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
procedure MimeEncode(var InputBuffer; const InputByteCount: Cardinal; var OutputBuffer);
|
|
||||||
var
|
var
|
||||||
B: Cardinal;
|
B: Cardinal;
|
||||||
InMax3: Cardinal;
|
InnerLimit, OuterLimit: TJclAddr;
|
||||||
InPtr, InLimitPtr: ^Byte;
|
InPtr: PByte3;
|
||||||
OutPtr: PByte4;
|
OutPtr: PByte4;
|
||||||
begin
|
begin
|
||||||
if InputByteCount <= 0 then
|
{ Do we have enough input to encode a full line? }
|
||||||
|
if InputByteCount < MIME_DECODED_LINE_BREAK then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
InPtr := @InputBuffer;
|
InPtr := @InputBuffer;
|
||||||
InMax3 := InputByteCount div 3 * 3;
|
OutPtr := @OutputBuffer;
|
||||||
OutPTr := @OutputBuffer;
|
|
||||||
Cardinal(InLimitPtr) := Cardinal(InPtr) + InMax3;
|
|
||||||
|
|
||||||
while InPtr <> InLimitPtr do
|
InnerLimit := TJclAddr(InPtr);
|
||||||
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
||||||
|
|
||||||
|
OuterLimit := TJclAddr(InPtr);
|
||||||
|
Inc(OuterLimit, InputByteCount);
|
||||||
|
|
||||||
|
{ Multiple line loop. }
|
||||||
|
repeat
|
||||||
|
{ Single line loop. }
|
||||||
|
repeat
|
||||||
|
{ Read 3 bytes from InputBuffer. }
|
||||||
|
B := InPtr^.B1;
|
||||||
|
B := B shl 8;
|
||||||
|
B := B or InPtr^.B2;
|
||||||
|
B := B shl 8;
|
||||||
|
B := B or InPtr^.B3;
|
||||||
|
Inc(InPtr);
|
||||||
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
||||||
|
OutPtr^.B4 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
|
B := B shr 6;
|
||||||
|
OutPtr^.B3 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
|
B := B shr 6;
|
||||||
|
OutPtr^.B2 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
|
B := B shr 6;
|
||||||
|
OutPtr^.B1 := MIME_ENCODE_TABLE[B];
|
||||||
|
Inc(OutPtr);
|
||||||
|
until TJclAddr(InPtr) >= InnerLimit;
|
||||||
|
|
||||||
|
{ Write line break (CRLF). }
|
||||||
|
OutPtr^.B1 := 13;
|
||||||
|
OutPtr^.B2 := 10;
|
||||||
|
Inc(TJclAddr(OutPtr), 2);
|
||||||
|
|
||||||
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
||||||
|
until InnerLimit > OuterLimit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure MimeEncodeNoCRLF(const InputBuffer; const InputByteCount: SizeInt; out OutputBuffer);
|
||||||
|
var
|
||||||
|
B: Cardinal;
|
||||||
|
InnerLimit, OuterLimit: SizeInt;
|
||||||
|
InPtr: PByte3;
|
||||||
|
OutPtr: PByte4;
|
||||||
|
begin
|
||||||
|
if InputByteCount = 0 then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
InPtr := @InputBuffer;
|
||||||
|
OutPtr := @OutputBuffer;
|
||||||
|
|
||||||
|
OuterLimit := InputByteCount div 3 * 3;
|
||||||
|
|
||||||
|
InnerLimit := TJclAddr(InPtr);
|
||||||
|
Inc(InnerLimit, OuterLimit);
|
||||||
|
|
||||||
|
{ Last line loop. }
|
||||||
|
while TJclAddr(InPtr) < TJclAddr(InnerLimit) do
|
||||||
begin
|
begin
|
||||||
B := InPtr^;
|
{ Read 3 bytes from InputBuffer. }
|
||||||
|
B := InPtr^.B1;
|
||||||
B := B shl 8;
|
B := B shl 8;
|
||||||
Inc(InPtr);
|
B := B or InPtr^.B2;
|
||||||
B := B or InPtr^;
|
|
||||||
B := B shl 8;
|
B := B shl 8;
|
||||||
|
B := B or InPtr^.B3;
|
||||||
Inc(InPtr);
|
Inc(InPtr);
|
||||||
B := B or InPtr^;
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
||||||
Inc(InPtr);
|
OutPtr^.B4 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
// Write 4 bytes to OutputBuffer (in reverse order).
|
|
||||||
OutPtr.B4 := MIME_ENCODE_TABLE[B and $3F];
|
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPtr.B3 := MIME_ENCODE_TABLE[B and $3F];
|
OutPtr^.B3 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
OutPtr^.B2 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
OutPtr^.B1 := MIME_ENCODE_TABLE[B];
|
||||||
Inc(OutPtr);
|
Inc(OutPtr);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
case InputByteCount - InMax3 of
|
{ End of data & padding. }
|
||||||
|
case InputByteCount - OuterLimit of
|
||||||
1:
|
1:
|
||||||
begin
|
begin
|
||||||
B := InPtr^;
|
B := InPtr^.B1;
|
||||||
B := B shl 4;
|
B := B shl 4;
|
||||||
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
||||||
OutPtr.B3 := EqualSign; // Fill remaining 2 bytes.
|
OutPtr.B3 := MIME_PAD_CHAR; { Pad remaining 2 bytes. }
|
||||||
OutPtr.B4 := EqualSign;
|
OutPtr.B4 := MIME_PAD_CHAR;
|
||||||
end;
|
end;
|
||||||
2:
|
2:
|
||||||
begin
|
begin
|
||||||
B := InPtr^;
|
B := InPtr^.B1;
|
||||||
Inc(InPtr);
|
|
||||||
B := B shl 8;
|
B := B shl 8;
|
||||||
B := B or InPtr^;
|
B := B or InPtr^.B2;
|
||||||
B := B shl 2;
|
B := B shl 2;
|
||||||
OutPtr.B3 := MIME_ENCODE_TABLE[B and $3F];
|
OutPtr.B3 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPTr.b2 := MIME_ENCODE_TABLE[B and $3F];
|
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
||||||
B := B shr 6;
|
B := B shr 6;
|
||||||
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
||||||
OutPtr.B4 := EqualSign; // Fill remaining byte.
|
OutPtr.B4 := MIME_PAD_CHAR; { Pad remaining byte. }
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeDecode(var InputBuffer; const InputBytesCount: Cardinal; var OutputBuffer): Cardinal;
|
procedure MimeEncode(const InputBuffer; const InputByteCount: SizeInt; out OutputBuffer);
|
||||||
var
|
var
|
||||||
ByteBuffer, ByteBufferSpace: Cardinal;
|
IDelta, ODelta: SizeInt;
|
||||||
|
I, O: PByte;
|
||||||
begin
|
begin
|
||||||
ByteBuffer := 0;
|
MimeEncodeFullLines(InputBuffer, InputByteCount, OutputBuffer);
|
||||||
ByteBufferSpace := 4;
|
IDelta := InputByteCount div MIME_DECODED_LINE_BREAK; // Number of lines processed so far.
|
||||||
Result := MimeDecodePartial(InputBuffer, InputBytesCount, OutputBuffer, ByteBuffer, ByteBufferSpace);
|
ODelta := IDelta * (MIME_ENCODED_LINE_BREAK + 2);
|
||||||
Inc(Result, MimeDecodePartialEnd(PAnsiChar(Cardinal(OutputBuffer) + Result)^, ByteBuffer, ByteBufferSpace));
|
IDelta := IDelta * MIME_DECODED_LINE_BREAK;
|
||||||
|
I := @InputBuffer;
|
||||||
|
Inc(I, IDelta);
|
||||||
|
O := @OutputBuffer;
|
||||||
|
Inc(O, ODelta);
|
||||||
|
MimeEncodeNoCRLF(I^, InputByteCount - IDelta, O^);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeDecodePartial(var InputBuffer; const InputBytesCount: Cardinal;
|
function MimeDecodePartial(const InputBuffer; const InputByteCount: SizeInt; out OutputBuffer;
|
||||||
var OutputBuffer; var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal;
|
var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): SizeInt;
|
||||||
var
|
var
|
||||||
lByteBuffer, lByteBufferSpace, C: Cardinal;
|
LByteBuffer, LByteBufferSpace, C: Cardinal;
|
||||||
InPtr, InLimitPtr: ^Byte;
|
InPtr, OuterLimit: PByte;
|
||||||
OutPtr: PByte3;
|
OutPtr: PByte3;
|
||||||
begin
|
begin
|
||||||
if InputBytesCount > 0 then
|
if InputByteCount > 0 then
|
||||||
begin
|
begin
|
||||||
InPtr := @InputBuffer;
|
InPtr := @InputBuffer;
|
||||||
Cardinal(InLimitPtr) := Cardinal(InPtr) + InputBytesCount;
|
OuterLimit := Pointer(TJclAddr(InPtr) + TJclAddr(InputByteCount));
|
||||||
OutPtr := @OutputBuffer;
|
OutPtr := @OutputBuffer;
|
||||||
lByteBuffer := ByteBuffer;
|
LByteBuffer := ByteBuffer;
|
||||||
lByteBufferSpace := ByteBufferSpace;
|
LByteBufferSpace := ByteBufferSpace;
|
||||||
while InPtr <> InLimitPtr do
|
while InPtr <> OuterLimit do
|
||||||
begin
|
begin
|
||||||
C := MIME_DECODE_TABLE[InPtr^]; // Read from InputBuffer.
|
{ Read from InputBuffer. }
|
||||||
|
C := MIME_DECODE_TABLE[InPtr^];
|
||||||
Inc(InPtr);
|
Inc(InPtr);
|
||||||
if C = $FF then
|
if C = $FF then
|
||||||
Continue;
|
Continue;
|
||||||
|
LByteBuffer := LByteBuffer shl 6;
|
||||||
|
LByteBuffer := LByteBuffer or C;
|
||||||
|
Dec(LByteBufferSpace);
|
||||||
|
{ Have we read 4 bytes from InputBuffer? }
|
||||||
|
if LByteBufferSpace <> 0 then
|
||||||
|
Continue;
|
||||||
|
|
||||||
lByteBuffer := lByteBuffer shl 6;
|
{ Write 3 bytes to OutputBuffer (in reverse order). }
|
||||||
lByteBuffer := lByteBuffer or C;
|
OutPtr^.B3 := Byte(LByteBuffer);
|
||||||
Dec(lByteBufferSpace);
|
LByteBuffer := LByteBuffer shr 8;
|
||||||
if lByteBufferSpace <> 0 then
|
OutPtr^.B2 := Byte(LByteBuffer);
|
||||||
Continue; // Read 4 bytes from InputBuffer?
|
LByteBuffer := LByteBuffer shr 8;
|
||||||
|
OutPtr^.B1 := Byte(LByteBuffer);
|
||||||
OutPtr.B3 := Byte(lByteBuffer); // Write 3 bytes to OutputBuffer (in reverse order).
|
LByteBuffer := 0;
|
||||||
lByteBuffer := lByteBuffer shr 8;
|
|
||||||
OutPtr.B2 := Byte(lByteBuffer);
|
|
||||||
lByteBuffer := lByteBuffer shr 8;
|
|
||||||
OutPtr.B1 := Byte(lByteBuffer);
|
|
||||||
lByteBuffer := 0;
|
|
||||||
Inc(OutPtr);
|
Inc(OutPtr);
|
||||||
lByteBufferSpace := 4;
|
LByteBufferSpace := 4;
|
||||||
end;
|
end;
|
||||||
ByteBuffer := lByteBuffer;
|
ByteBuffer := LByteBuffer;
|
||||||
ByteBufferSpace := lByteBufferSpace;
|
ByteBufferSpace := LByteBufferSpace;
|
||||||
Result := Cardinal(OutPtr) - Cardinal(@OutputBuffer);
|
Result := SizeInt(TJclAddr(OutPtr) - TJclAddr(@OutputBuffer));
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function MimeDecodePartialEnd(var OutputBuffer; const ByteBuffer: Cardinal;
|
function MimeDecodePartialEnd(out OutputBuffer; const ByteBuffer: Cardinal;
|
||||||
const ByteBufferSpace: Cardinal): Cardinal;
|
const ByteBufferSpace: Cardinal): SizeInt;
|
||||||
var
|
var
|
||||||
lByteBuffer: Cardinal;
|
LByteBuffer: Cardinal;
|
||||||
begin
|
begin
|
||||||
case ByteBufferSpace of
|
case ByteBufferSpace of
|
||||||
1:
|
1:
|
||||||
begin
|
begin
|
||||||
lByteBuffer := ByteBuffer shr 2;
|
LByteBuffer := ByteBuffer shr 2;
|
||||||
PByte3(@OutputBuffer).B2 := Byte(lByteBuffer);
|
PByte3(@OutputBuffer)^.B2 := Byte(LByteBuffer);
|
||||||
lByteBuffer := lByteBuffer shr 8;
|
LByteBuffer := LByteBuffer shr 8;
|
||||||
PByte3(@OutputBuffer).B1 := Byte(lByteBuffer);
|
PByte3(@OutputBuffer)^.B1 := Byte(LByteBuffer);
|
||||||
Result := 2;
|
Result := 2;
|
||||||
end;
|
end;
|
||||||
2:
|
2:
|
||||||
begin
|
begin
|
||||||
lByteBuffer := ByteBuffer shr 4;
|
LByteBuffer := ByteBuffer shr 4;
|
||||||
PByte3(@OutputBuffer).B1 := Byte(lByteBuffer);
|
PByte3(@OutputBuffer)^.B1 := Byte(LByteBuffer);
|
||||||
Result := 1;
|
Result := 1;
|
||||||
end;
|
end;
|
||||||
else
|
else
|
||||||
@ -906,5 +879,106 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function MimeEncodeString(const S: AnsiString): AnsiString;
|
||||||
|
var
|
||||||
|
L: SizeInt;
|
||||||
|
begin
|
||||||
|
if S <> '' then
|
||||||
|
begin
|
||||||
|
L := Length(S);
|
||||||
|
SetLength(Result, MimeEncodedSize(L));
|
||||||
|
MimeEncode(PAnsiChar(S)^, L, PAnsiChar(Result)^);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function MimeDecodedSize(const InputSize: SizeInt): SizeInt;
|
||||||
|
begin
|
||||||
|
Result := (InputSize + 3) div 4 * 3;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function MimeDecodeString(const S: AnsiString): AnsiString;
|
||||||
|
var
|
||||||
|
ByteBuffer, ByteBufferSpace: Cardinal;
|
||||||
|
L: SizeInt;
|
||||||
|
P, R: PAnsiChar;
|
||||||
|
begin
|
||||||
|
if S <> '' then
|
||||||
|
begin
|
||||||
|
L := Length(S);
|
||||||
|
SetLength(Result, MimeDecodedSize(L));
|
||||||
|
ByteBuffer := 0;
|
||||||
|
ByteBufferSpace := 4;
|
||||||
|
P := PAnsiChar(S);
|
||||||
|
R := PAnsiChar(Result);
|
||||||
|
L := MimeDecodePartial(P^, L, R^, ByteBuffer, ByteBufferSpace);
|
||||||
|
Inc(R, L);
|
||||||
|
Inc(L, MimeDecodePartialEnd(R^, ByteBuffer, ByteBufferSpace));
|
||||||
|
SetLength(Result, L);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream);
|
||||||
|
var
|
||||||
|
InputBuffer: array [0..MIME_BUFFER_SIZE - 1] of Byte;
|
||||||
|
OutputBuffer: array [0..(MIME_BUFFER_SIZE + 2) div 3 * 4 + MIME_BUFFER_SIZE div MIME_DECODED_LINE_BREAK * 2 - 1] of Byte;
|
||||||
|
BytesRead: SizeInt;
|
||||||
|
IDelta, ODelta: SizeInt;
|
||||||
|
I, O: PByte;
|
||||||
|
begin
|
||||||
|
InputBuffer[0] := 0;
|
||||||
|
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
||||||
|
|
||||||
|
while BytesRead = Length(InputBuffer) do
|
||||||
|
begin
|
||||||
|
MimeEncodeFullLines(InputBuffer, Length(InputBuffer), OutputBuffer);
|
||||||
|
OutputStream.Write(OutputBuffer, Length(OutputBuffer));
|
||||||
|
BytesRead := InputStream.Read(InputBuffer, Length(InputBuffer));
|
||||||
|
end;
|
||||||
|
|
||||||
|
MimeEncodeFullLines(InputBuffer, BytesRead, OutputBuffer);
|
||||||
|
|
||||||
|
IDelta := BytesRead div MIME_DECODED_LINE_BREAK; // Number of lines processed.
|
||||||
|
ODelta := IDelta * (MIME_ENCODED_LINE_BREAK + 2);
|
||||||
|
IDelta := IDelta * MIME_DECODED_LINE_BREAK;
|
||||||
|
|
||||||
|
I := @InputBuffer;
|
||||||
|
Inc(I, IDelta);
|
||||||
|
O := @OutputBuffer;
|
||||||
|
Inc(O, ODelta);
|
||||||
|
|
||||||
|
MimeEncodeNoCRLF(I^, BytesRead - IDelta, O^);
|
||||||
|
|
||||||
|
OutputStream.Write(OutputBuffer, MimeEncodedSize(BytesRead));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream);
|
||||||
|
var
|
||||||
|
ByteBuffer, ByteBufferSpace: Cardinal;
|
||||||
|
InputBuffer: array [0..MIME_BUFFER_SIZE - 1] of Byte;
|
||||||
|
OutputBuffer: array [0..(MIME_BUFFER_SIZE + 3) div 4 * 3 - 1] of Byte;
|
||||||
|
BytesRead: SizeInt;
|
||||||
|
begin
|
||||||
|
ByteBuffer := 0;
|
||||||
|
ByteBufferSpace := 4;
|
||||||
|
InputBuffer[0] := 0;
|
||||||
|
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
||||||
|
|
||||||
|
while BytesRead > 0 do
|
||||||
|
begin
|
||||||
|
OutputStream.Write(OutputBuffer, MimeDecodePartial(InputBuffer, BytesRead, OutputBuffer, ByteBuffer, ByteBufferSpace));
|
||||||
|
BytesRead := InputStream.Read(InputBuffer, Length(InputBuffer));
|
||||||
|
end;
|
||||||
|
OutputStream.Write(OutputBuffer, MimeDecodePartialEnd(OutputBuffer, ByteBuffer, ByteBufferSpace));
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user