2229 lines
61 KiB
ObjectPascal
2229 lines
61 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvStrings.PAS, released on 2002-06-15.
|
|
|
|
The Initial Developer of the Original Code is Jan Verhoeven [jan1 dott verhoeven att wxs dott nl]
|
|
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s): Robert Love [rlove att slcdug dott org].
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
Should be merged with JCL
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvStrings.pas 10612 2006-05-19 19:04:09Z jfudickar $
|
|
|
|
unit JvStrings;
|
|
|
|
{$I jvcl.inc}
|
|
{$I crossplatform.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
{$IFDEF MSWINDOWS}
|
|
Windows,
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF HAS_UNIT_LIBC}
|
|
Libc,
|
|
{$ENDIF HAS_UNIT_LIBC}
|
|
Graphics,
|
|
SysUtils, Classes;
|
|
|
|
{regular expressions}
|
|
|
|
{template functions}
|
|
function ReplaceFirst(const SourceStr, FindStr, ReplaceStr: string): string;
|
|
function ReplaceLast(const SourceStr, FindStr, ReplaceStr: string): string;
|
|
function InsertLastBlock(var SourceStr: string; BlockStr: string): Boolean;
|
|
function RemoveMasterBlocks(const SourceStr: string): string;
|
|
function RemoveFields(const SourceStr: string): string;
|
|
|
|
{http functions}
|
|
function URLEncode(const Value: string): string; // Converts string To A URLEncoded string
|
|
function URLDecode(const Value: string): string; // Converts string From A URLEncoded string
|
|
|
|
{set functions}
|
|
procedure SplitSet(AText: string; AList: TStringList);
|
|
function JoinSet(AList: TStringList): string;
|
|
function FirstOfSet(const AText: string): string;
|
|
function LastOfSet(const AText: string): string;
|
|
function CountOfSet(const AText: string): Integer;
|
|
function SetRotateRight(const AText: string): string;
|
|
function SetRotateLeft(const AText: string): string;
|
|
function SetPick(const AText: string; AIndex: Integer): string;
|
|
function SetSort(const AText: string): string;
|
|
function SetUnion(const Set1, Set2: string): string;
|
|
function SetIntersect(const Set1, Set2: string): string;
|
|
function SetExclude(const Set1, Set2: string): string;
|
|
|
|
{replace any <,> etc by < >}
|
|
function XMLSafe(const AText: string): string;
|
|
|
|
{simple hash, Result can be used in Encrypt}
|
|
function Hash(const AText: string): Integer;
|
|
|
|
{ Base64 encode and decode a string }
|
|
function B64Encode(const S: string): string;
|
|
function B64Decode(const S: string): string;
|
|
|
|
{Basic encryption from a Borland Example}
|
|
function Encrypt(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
function Decrypt(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
|
|
{Using Encrypt and Decrypt in combination with B64Encode and B64Decode}
|
|
function EncryptB64(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
function DecryptB64(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
|
|
procedure CSVToTags(Src, Dst: TStringList);
|
|
// converts a csv list to a tagged string list
|
|
|
|
procedure TagsToCSV(Src, Dst: TStringList);
|
|
// converts a tagged string list to a csv list
|
|
// only fieldnames from the first record are scanned ib the other records
|
|
|
|
procedure ListSelect(Src, Dst: TStringList; const AKey, AValue: string);
|
|
{selects akey=avalue from Src and returns recordset in Dst}
|
|
|
|
procedure ListFilter(Src: TStringList; const AKey, AValue: string);
|
|
{filters Src for akey=avalue}
|
|
|
|
procedure ListOrderBy(Src: TStringList; const AKey: string; Numeric: Boolean);
|
|
{orders a tagged Src list by akey}
|
|
|
|
function PosStr(const FindString, SourceString: string;
|
|
StartPos: Integer = 1): Integer;
|
|
{ PosStr searches the first occurrence of a substring FindString in a string
|
|
given by SourceString with case sensitivity (upper and lower case characters
|
|
are differed). This function returns the index value of the first character
|
|
of a specified substring from which it occurs in a given string starting with
|
|
StartPos character index. If a specified substring is not found Q_PosStr
|
|
returns zero. The author of algorithm is Peter Morris (UK) (Faststrings unit
|
|
from www.torry.ru). }
|
|
|
|
function PosStrLast(const FindString, SourceString: string): Integer;
|
|
{finds the last occurance}
|
|
|
|
function LastPosChar(const FindChar: Char; SourceString: string): Integer;
|
|
|
|
function PosText(const FindString, SourceString: string;
|
|
StartPos: Integer = 1): Integer;
|
|
{ PosText searches the first occurrence of a substring FindString in a string
|
|
given by SourceString without case sensitivity (upper and lower case
|
|
characters are not differed). This function returns the index value of the
|
|
first character of a specified substring from which it occurs in a given
|
|
string starting with StartPos character index. If a specified substring is
|
|
not found Q_PosStr returns zero. The author of algorithm is Peter Morris
|
|
(UK) (Faststrings unit from www.torry.ru). }
|
|
|
|
function PosTextLast(const FindString, SourceString: string): Integer;
|
|
{finds the last occurance}
|
|
|
|
function NameValuesToXML(const AText: string): string;
|
|
{$IFDEF MSWINDOWS}
|
|
procedure LoadResourceFile(AFile: string; MemStream: TMemoryStream);
|
|
{$ENDIF MSWINDOWS}
|
|
procedure DirFiles(const ADir, AMask: string; AFileList: TStringList);
|
|
procedure RecurseDirFiles(const ADir: string; var AFileList: TStringList);
|
|
procedure RecurseDirProgs(const ADir: string; var AFileList: TStringList);
|
|
procedure SaveString(const AFile, AText: string);
|
|
function LoadString(const AFile: string): string;
|
|
function HexToColor(const AText: string): TColor;
|
|
function UppercaseHTMLTags(const AText: string): string;
|
|
function LowercaseHTMLTags(const AText: string): string;
|
|
procedure GetHTMLAnchors(const AFile: string; AList: TStringList);
|
|
function RelativePath(const ASrc, ADst: string): string;
|
|
function GetToken(var Start: Integer; const SourceText: string): string;
|
|
function PosNonSpace(Start: Integer; const SourceText: string): Integer;
|
|
function PosEscaped(Start: Integer; const SourceText, FindText: string; EscapeChar: Char): Integer;
|
|
function DeleteEscaped(const SourceText: string; EscapeChar: Char): string;
|
|
function BeginOfAttribute(Start: Integer; const SourceText: string): Integer;
|
|
// parses the beginning of an attribute: space + alpha character
|
|
function ParseAttribute(var Start: Integer; const SourceText: string; var AName, AValue: string): Boolean;
|
|
// parses a name="value" attribute from Start; returns 0 when not found or else the position behind the attribute
|
|
procedure ParseAttributes(const SourceText: string; Attributes: TStrings);
|
|
// parses all name=value attributes to the attributes TStringList
|
|
function HasStrValue(const AText, AName: string; var AValue: string): Boolean;
|
|
// checks if a name="value" pair exists and returns any value
|
|
function GetStrValue(const AText, AName, ADefault: string): string;
|
|
// retrieves string value from a line like:
|
|
// name="jan verhoeven" email="jan1 dott verhoeven att wxs dott nl"
|
|
// returns ADefault when not found
|
|
function GetHTMLColorValue(const AText, AName: string; ADefault: TColor): TColor;
|
|
// same for a color
|
|
function GetIntValue(const AText, AName: string; ADefault: Integer): Integer;
|
|
// same for an Integer
|
|
function GetFloatValue(const AText, AName: string; ADefault: Extended): Extended;
|
|
// same for a float
|
|
function GetBoolValue(const AText, AName: string): Boolean;
|
|
// same for Boolean but without default
|
|
function GetValue(const AText, AName: string): string;
|
|
// retrieves string value from a line like:
|
|
// name="jan verhoeven" email="jan1 dott verhoeven att wxs dott nl"
|
|
procedure SetValue(var AText: string; const AName, AValue: string);
|
|
// sets a string value in a line
|
|
procedure DeleteValue(var AText: string; const AName: string);
|
|
// deletes a AName="value" pair from AText
|
|
|
|
procedure GetNames(AText: string; AList: TStringList);
|
|
// get a list of names from a string with name="value" pairs
|
|
function GetHTMLColor(AColor: TColor): string;
|
|
// converts a color value to the HTML hex value
|
|
function BackPosStr(Start: Integer; const FindString, SourceString: string): Integer;
|
|
// finds a string backward case sensitive
|
|
function BackPosText(Start: Integer; const FindString, SourceString: string): Integer;
|
|
// finds a string backward case insensitive
|
|
function PosRangeStr(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
// finds a text range, e.g. <TD>....</TD> case sensitive
|
|
function PosRangeText(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
// finds a text range, e.g. <TD>....</td> case insensitive
|
|
function BackPosRangeStr(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
// finds a text range backward, e.g. <TD>....</TD> case sensitive
|
|
function BackPosRangeText(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
// finds a text range backward, e.g. <TD>....</td> case insensitive
|
|
function PosTag(Start: Integer; SourceString: string; var RangeBegin: Integer;
|
|
var RangeEnd: Integer): Boolean;
|
|
// finds a HTML or XML tag: <....>
|
|
function InnerTag(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
// finds the innertext between opening and closing tags
|
|
function Easter(NYear: Integer): TDateTime;
|
|
// returns the easter date of a year.
|
|
function GetWeekNumber(Today: TDateTime): string;
|
|
//gets a datecode. Returns year and weeknumber in format: YYWW
|
|
|
|
function ParseNumber(const S: string): Integer;
|
|
// parse number returns the last position, starting from 1
|
|
function ParseDate(const S: string): Integer;
|
|
// parse a SQL style data string from positions 1,
|
|
// starts and ends with #
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvStrings.pas $';
|
|
Revision: '$Revision: 10612 $';
|
|
Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
JvConsts, JvResources, JvTypes;
|
|
|
|
const
|
|
B64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
ValidURLChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$-_@.&+-!*"''(),;/#?:';
|
|
|
|
ToUpperChars: array [0..255] of Char =
|
|
(#$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07, #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
|
|
#$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17, #$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
|
|
#$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27, #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
|
|
#$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37, #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
|
|
#$40, #$41, #$42, #$43, #$44, #$45, #$46, #$47, #$48, #$49, #$4A, #$4B, #$4C, #$4D, #$4E, #$4F,
|
|
#$50, #$51, #$52, #$53, #$54, #$55, #$56, #$57, #$58, #$59, #$5A, #$5B, #$5C, #$5D, #$5E, #$5F,
|
|
#$60, #$41, #$42, #$43, #$44, #$45, #$46, #$47, #$48, #$49, #$4A, #$4B, #$4C, #$4D, #$4E, #$4F,
|
|
#$50, #$51, #$52, #$53, #$54, #$55, #$56, #$57, #$58, #$59, #$5A, #$7B, #$7C, #$7D, #$7E, #$7F,
|
|
#$80, #$81, #$82, #$81, #$84, #$85, #$86, #$87, #$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
|
|
#$80, #$91, #$92, #$93, #$94, #$95, #$96, #$97, #$98, #$99, #$8A, #$9B, #$8C, #$8D, #$8E, #$8F,
|
|
#$A0, #$A1, #$A1, #$A3, #$A4, #$A5, #$A6, #$A7, #$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
|
|
#$B0, #$B1, #$B2, #$B2, #$A5, #$B5, #$B6, #$B7, #$A8, #$B9, #$AA, #$BB, #$A3, #$BD, #$BD, #$AF,
|
|
#$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
|
|
#$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7, #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
|
|
#$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
|
|
#$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7, #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF);
|
|
|
|
(* make Delphi 5 compiler happy // andreas
|
|
ToLowerChars: array[0..255] of Char =
|
|
(#$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07, #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
|
|
#$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17, #$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
|
|
#$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27, #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
|
|
#$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37, #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
|
|
#$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
|
|
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F,
|
|
#$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
|
|
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
|
|
#$90, #$83, #$82, #$83, #$84, #$85, #$86, #$87, #$88, #$89, #$9A, #$8B, #$9C, #$9D, #$9E, #$9F,
|
|
#$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97, #$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
|
|
#$A0, #$A2, #$A2, #$BC, #$A4, #$B4, #$A6, #$A7, #$B8, #$A9, #$BA, #$AB, #$AC, #$AD, #$AE, #$BF,
|
|
#$B0, #$B1, #$B3, #$B3, #$B4, #$B5, #$B6, #$B7, #$B8, #$B9, #$BA, #$BB, #$BC, #$BE, #$BE, #$BF,
|
|
#$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
|
|
#$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7, #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF,
|
|
#$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
|
|
#$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7, #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF);
|
|
*)
|
|
|
|
procedure SaveString(const AFile, AText: string);
|
|
begin
|
|
with TFileStream.Create(AFile, fmCreate) do
|
|
try
|
|
WriteBuffer(AText[1], Length(AText));
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
function LoadString(const AFile: string): string;
|
|
var
|
|
S: string;
|
|
begin
|
|
with TFileStream.Create(AFile, fmOpenRead) do
|
|
try
|
|
SetLength(S, Size);
|
|
ReadBuffer(S[1], Size);
|
|
finally
|
|
Free;
|
|
end;
|
|
Result := S;
|
|
end;
|
|
|
|
procedure DeleteValue(var AText: string; const AName: string);
|
|
var
|
|
P, P2, L: Integer;
|
|
begin
|
|
L := Length(AName) + 2;
|
|
P := PosText(AName + '="', AText);
|
|
if P = 0 then
|
|
Exit;
|
|
P2 := PosStr('"', AText, P + L);
|
|
if P2 = 0 then
|
|
Exit;
|
|
if P > 1 then
|
|
Dec(P); // include the preceding space if not the first one
|
|
Delete(AText, P, P2 - P + 1);
|
|
end;
|
|
|
|
function GetValue(const AText, AName: string): string;
|
|
var
|
|
P, P2, L: Integer;
|
|
begin
|
|
Result := '';
|
|
L := Length(AName) + 2;
|
|
P := PosText(AName + '="', AText);
|
|
if P = 0 then
|
|
Exit;
|
|
P2 := PosStr('"', AText, P + L);
|
|
if P2 = 0 then
|
|
Exit;
|
|
Result := Copy(AText, P + L, P2 - (P + L));
|
|
Result := StringReplace(Result, '~~', Cr, [rfReplaceAll]);
|
|
end;
|
|
|
|
function HasStrValue(const AText, AName: string; var AValue: string): Boolean;
|
|
var
|
|
P, P2, L: Integer;
|
|
S: string;
|
|
begin
|
|
Result := False;
|
|
L := Length(AName) + 2;
|
|
P := PosText(AName + '="', AText);
|
|
if P = 0 then
|
|
Exit;
|
|
P2 := PosStr('"', AText, P + L);
|
|
if P2 = 0 then
|
|
Exit;
|
|
S := Copy(AText, P + L, P2 - (P + L));
|
|
AValue := StringReplace(S, '~~', Cr, [rfReplaceAll]);
|
|
Result := False;
|
|
end;
|
|
|
|
function GetStrValue(const AText, AName, ADefault: string): string;
|
|
var
|
|
S: string;
|
|
begin
|
|
S := '';
|
|
if HasStrValue(AText, AName, S) then
|
|
Result := S
|
|
else
|
|
Result := ADefault;
|
|
end;
|
|
|
|
function GetIntValue(const AText, AName: string; ADefault: Integer): Integer;
|
|
var
|
|
S: string;
|
|
begin
|
|
S := GetValue(AText, AName);
|
|
try
|
|
Result := StrToInt(S);
|
|
except
|
|
Result := ADefault;
|
|
end;
|
|
end;
|
|
|
|
function GetFloatValue(const AText, AName: string; ADefault: Extended): Extended;
|
|
var
|
|
S: string;
|
|
begin
|
|
S := '';
|
|
if HasStrValue(AText, AName, S) then
|
|
try
|
|
Result := StrToFloat(S);
|
|
except
|
|
Result := ADefault;
|
|
end
|
|
else
|
|
Result := ADefault;
|
|
end;
|
|
|
|
function GetHTMLColorValue(const AText, AName: string; ADefault: TColor): TColor;
|
|
var
|
|
S: string;
|
|
begin
|
|
S := '';
|
|
if HasStrValue(AText, AName, S) then
|
|
begin
|
|
if Copy(S, 1, 1) = '#' then
|
|
S := '$' + Copy(S, 6, 2) + Copy(S, 4, 2) + Copy(S, 2, 2)
|
|
else
|
|
S := 'cl' + S;
|
|
try
|
|
Result := StringToColor(S);
|
|
except
|
|
Result := ADefault;
|
|
end;
|
|
end
|
|
else
|
|
Result := ADefault;
|
|
end;
|
|
|
|
procedure SetValue(var AText: string; const AName, AValue: string);
|
|
var
|
|
P, P2, L: Integer;
|
|
begin
|
|
L := Length(AName) + 2;
|
|
if AText = '' then
|
|
AText := AName + '="' + AValue + '"'
|
|
else
|
|
begin
|
|
P := PosText(AName + '="', AText);
|
|
if P = 0 then
|
|
AText := AText + ' ' + AName + '="' + AValue + '"'
|
|
else
|
|
begin
|
|
P2 := PosStr('"', AText, P + L);
|
|
if P2 = 0 then
|
|
Exit;
|
|
Delete(AText, P + L, P2 - (P + L));
|
|
Insert(AValue, AText, P + L);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function GetHTMLColor(AColor: TColor): string;
|
|
begin
|
|
Result := Format('%6.6x', [ColorToRGB(AColor)]);
|
|
Result := '="#' + Copy(Result, 5, 2) + Copy(Result, 3, 2) + Copy(Result, 1, 2) + '"';
|
|
end;
|
|
|
|
function BackPosStr(Start: Integer; const FindString, SourceString: string): Integer;
|
|
var
|
|
P, L: Integer;
|
|
begin
|
|
Result := 0;
|
|
L := Length(FindString);
|
|
if (L = 0) or (SourceString = '') or (Start < 2) then
|
|
Exit;
|
|
Start := Start - L;
|
|
if Start < 1 then
|
|
Exit;
|
|
repeat
|
|
P := PosStr(FindString, SourceString, Start);
|
|
if P < Start then
|
|
begin
|
|
Result := P;
|
|
Exit;
|
|
end;
|
|
Start := Start - L;
|
|
until Start < 1;
|
|
end;
|
|
|
|
function BackPosText(Start: Integer; const FindString, SourceString: string): Integer;
|
|
var
|
|
P, L, From: Integer;
|
|
begin
|
|
Result := 0;
|
|
L := Length(FindString);
|
|
if (L = 0) or (SourceString = '') or (Start < 2) then
|
|
Exit;
|
|
From := Start - L;
|
|
if From < 1 then
|
|
Exit;
|
|
repeat
|
|
P := PosText(FindString, SourceString, From);
|
|
if P < Start then
|
|
begin
|
|
Result := P;
|
|
Exit;
|
|
end;
|
|
From := From - L;
|
|
until From < 1;
|
|
end;
|
|
|
|
function PosRangeStr(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
RangeBegin := PosStr(HeadString, SourceString, Start);
|
|
if RangeBegin = 0 then
|
|
Exit;
|
|
RangeEnd := PosStr(TailString, SourceString, RangeBegin + Length(HeadString));
|
|
if RangeEnd = 0 then
|
|
Exit;
|
|
RangeEnd := RangeEnd + Length(TailString) - 1;
|
|
Result := True;
|
|
end;
|
|
|
|
function PosRangeText(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
RangeBegin := PosText(HeadString, SourceString, Start);
|
|
if RangeBegin = 0 then
|
|
Exit;
|
|
RangeEnd := PosText(TailString, SourceString, RangeBegin + Length(HeadString));
|
|
if RangeEnd = 0 then
|
|
Exit;
|
|
RangeEnd := RangeEnd + Length(TailString) - 1;
|
|
Result := True;
|
|
end;
|
|
|
|
function InnerTag(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
RangeBegin := PosText(HeadString, SourceString, Start);
|
|
if RangeBegin = 0 then
|
|
Exit;
|
|
RangeBegin := RangeBegin + Length(HeadString);
|
|
RangeEnd := PosText(TailString, SourceString, RangeBegin + Length(HeadString));
|
|
if RangeEnd = 0 then
|
|
Exit;
|
|
RangeEnd := RangeEnd - 1;
|
|
Result := True;
|
|
end;
|
|
|
|
function PosTag(Start: Integer; SourceString: string; var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
begin
|
|
Result := PosRangeStr(Start, '<', '>', SourceString, RangeBegin, RangeEnd);
|
|
end;
|
|
|
|
function BackPosRangeStr(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
var
|
|
L: Integer;
|
|
begin
|
|
// finds a text range backward, e.g. <TD>....</TD> case sensitive
|
|
Result := False;
|
|
L := Length(HeadString);
|
|
if (L = 0) or (Start < 2) then
|
|
Exit;
|
|
Start := Start - L;
|
|
if Start < 1 then
|
|
Exit;
|
|
repeat
|
|
if not PosRangeStr(Start, HeadString, TailString, SourceString, RangeBegin, RangeEnd) then
|
|
Exit;
|
|
if RangeBegin < Start then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
Start := Start - L;
|
|
until Start < 1;
|
|
end;
|
|
|
|
function BackPosRangeText(Start: Integer; const HeadString, TailString, SourceString: string;
|
|
var RangeBegin: Integer; var RangeEnd: Integer): Boolean;
|
|
var
|
|
L: Integer;
|
|
begin
|
|
// finds a text range backward, e.g. <TD>....</TD> case insensitive
|
|
Result := False;
|
|
L := Length(HeadString);
|
|
if (L = 0) or (Start < 2) then
|
|
Exit;
|
|
Start := Start - L;
|
|
if Start < 1 then
|
|
Exit;
|
|
repeat
|
|
if not PosRangeText(Start, HeadString, TailString, SourceString, RangeBegin, RangeEnd) then
|
|
Exit;
|
|
if RangeBegin < Start then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
Start := Start - L;
|
|
until Start < 1;
|
|
end;
|
|
|
|
function PosNonSpace(Start: Integer; const SourceText: string): Integer;
|
|
var
|
|
P, L: Integer;
|
|
begin
|
|
Result := 0;
|
|
L := Length(SourceText);
|
|
P := Start;
|
|
if L = 0 then
|
|
Exit;
|
|
while (P < L) and (SourceText[P] = ' ') do
|
|
Inc(P);
|
|
if SourceText[P] <> ' ' then
|
|
Result := P;
|
|
end;
|
|
|
|
function BeginOfAttribute(Start: Integer; const SourceText: string): Integer;
|
|
var
|
|
P, L: Integer;
|
|
begin
|
|
// parses the beginning of an attribute: space + alpha character
|
|
Result := 0;
|
|
L := Length(SourceText);
|
|
if L = 0 then
|
|
Exit;
|
|
P := PosStr(' ', SourceText, Start);
|
|
if P = 0 then
|
|
Exit;
|
|
P := PosNonSpace(P, SourceText);
|
|
if P = 0 then
|
|
Exit;
|
|
if SourceText[P] in ['a'..'z', 'A'..'Z'] then
|
|
Result := P;
|
|
end;
|
|
|
|
function ParseAttribute(var Start: Integer; const SourceText: string;
|
|
var AName, AValue: string): Boolean;
|
|
var
|
|
PN, PV, P: Integer;
|
|
begin
|
|
// parses a name="value" attribute from Start; returns 0 when not found or else the position behind the attribute
|
|
Result := False;
|
|
PN := BeginOfAttribute(Start, SourceText);
|
|
if PN = 0 then
|
|
Exit;
|
|
P := PosStr('="', SourceText, PN);
|
|
if P = 0 then
|
|
Exit;
|
|
AName := Trim(Copy(SourceText, PN, P - PN));
|
|
PV := P + 2;
|
|
P := PosStr('"', SourceText, PV);
|
|
if P = 0 then
|
|
Exit;
|
|
AValue := Copy(SourceText, PV, P - PV);
|
|
Start := P + 1;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure ParseAttributes(const SourceText: string; Attributes: TStrings);
|
|
var
|
|
Name, Value: string;
|
|
Start: Integer;
|
|
begin
|
|
Attributes.BeginUpdate;
|
|
try
|
|
Attributes.Clear;
|
|
Start := 1;
|
|
while ParseAttribute(Start, SourceText, Name, Value) do
|
|
Attributes.Add(Name + '=' + Value);
|
|
finally
|
|
Attributes.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function GetToken(var Start: Integer; const SourceText: string): string;
|
|
var
|
|
P1, P2: Integer;
|
|
begin
|
|
Result := '';
|
|
if Start > Length(SourceText) then
|
|
Exit;
|
|
P1 := PosNonSpace(Start, SourceText);
|
|
if P1 = 0 then
|
|
Exit;
|
|
if SourceText[P1] = '"' then
|
|
begin // quoted token
|
|
P2 := PosStr('"', SourceText, P1 + 1);
|
|
if P2 = 0 then
|
|
Exit;
|
|
Result := Copy(SourceText, P1 + 1, P2 - P1 - 1);
|
|
Start := P2 + 1;
|
|
end
|
|
else
|
|
begin
|
|
P2 := PosStr(' ', SourceText, P1 + 1);
|
|
if P2 = 0 then
|
|
P2 := Length(SourceText) + 1;
|
|
Result := Copy(SourceText, P1, P2 - P1);
|
|
Start := P2;
|
|
end;
|
|
end;
|
|
|
|
function Easter(NYear: Integer): TDateTime;
|
|
var
|
|
NMonth, NDay, NMoon, NEpact, NSunday, NGold, NCent, NCorX, NCorZ: Integer;
|
|
begin
|
|
|
|
{ The Golden Number of the year in the 19 year Metonic Cycle }
|
|
NGold := ((NYear mod 19) + 1);
|
|
|
|
{ Calculate the Century }
|
|
NCent := ((NYear div 100) + 1);
|
|
|
|
{ No. of Years in which leap year was dropped in order to keep in step
|
|
with the sun }
|
|
NCorX := ((3 * NCent) div 4 - 12);
|
|
|
|
{ Special Correction to Syncronize Easter with the moon's orbit }
|
|
NCorZ := ((8 * NCent + 5) div 25 - 5);
|
|
|
|
{ Find Sunday }
|
|
NSunday := ((5 * NYear) div 4 - NCorX - 10);
|
|
|
|
{ Set Epact (specifies occurance of full moon }
|
|
NEpact := ((11 * NGold + 20 + NCorZ - NCorX) mod 30);
|
|
|
|
if (NEpact < 0) then
|
|
NEpact := NEpact + 30;
|
|
|
|
if ((NEpact = 25) and (NGold > 11)) or (NEpact = 24) then
|
|
NEpact := NEpact + 1;
|
|
|
|
{ Find Full Moon }
|
|
NMoon := 44 - NEpact;
|
|
|
|
if (NMoon < 21) then
|
|
NMoon := NMoon + 30;
|
|
|
|
{ Advance to Sunday }
|
|
NMoon := (NMoon + 7 - ((NSunday + NMoon) mod 7));
|
|
|
|
if (NMoon > 31) then
|
|
begin
|
|
NMonth := 4;
|
|
NDay := (NMoon - 31);
|
|
end
|
|
else
|
|
begin
|
|
NMonth := 3;
|
|
NDay := NMoon;
|
|
end;
|
|
|
|
Result := EncodeDate(NYear, NMonth, NDay);
|
|
end;
|
|
|
|
//gets a datecode. Returns year and weeknumber in format: YYWW
|
|
|
|
{DayOfWeek function returns Integer 1..7 equivalent to Sunday..Saturday.
|
|
ISO 8601 weeks Start with Monday and the first week of a year is the one which
|
|
includes the first Thursday - Fiddle takes care of all this}
|
|
|
|
function GetWeekNumber(Today: TDateTime): string;
|
|
const
|
|
Fiddle: array [1..7] of Byte = (6, 7, 8, 9, 10, 4, 5);
|
|
var
|
|
Present, StartOfYear: TDateTime;
|
|
FirstDayOfYear, WeekNumber, NumberOfDays: Integer;
|
|
Year, Month, Day: Word;
|
|
YearNumber: string;
|
|
begin
|
|
Present := Trunc(Today); //truncate to remove hours, mins and secs
|
|
DecodeDate(Present, Year, Month, Day); //decode to find year
|
|
StartOfYear := EncodeDate(Year, 1, 1); //encode 1st Jan of the year
|
|
|
|
//find what day of week 1st Jan is, then add days according to rule
|
|
FirstDayOfYear := Fiddle[DayOfWeek(StartOfYear)];
|
|
|
|
//calc number of days since beginning of year + additional according to rule
|
|
NumberOfDays := Trunc(Present - StartOfYear) + FirstDayOfYear;
|
|
|
|
//calc number of weeks
|
|
WeekNumber := Trunc(NumberOfDays / 7);
|
|
|
|
//Format year, needed to prevent millenium bug and keep the Fluffy Spangle happy
|
|
YearNumber := FormatDateTime('yyyy', Present);
|
|
|
|
YearNumber := YearNumber + 'W';
|
|
|
|
if WeekNumber < 10 then
|
|
YearNumber := YearNumber + '0'; //add leading zero for week
|
|
|
|
//create datecode string
|
|
Result := YearNumber + IntToStr(WeekNumber);
|
|
|
|
if WeekNumber = 0 then //recursive call for year begin/end...
|
|
//see if previous year end was week 52 or 53
|
|
Result := GetWeekNumber(EncodeDate(Year - 1, 12, 31))
|
|
else
|
|
if WeekNumber = 53 then
|
|
//if 31st December less than Thursday then must be week 01 of next year
|
|
if DayOfWeek(EncodeDate(Year, 12, 31)) < 5 then
|
|
begin
|
|
YearNumber := FormatDateTime('yyyy', EncodeDate(Year + 1, 1, 1));
|
|
Result := YearNumber + 'W01';
|
|
end;
|
|
end;
|
|
|
|
function RelativePath(const ASrc, ADst: string): string;
|
|
var
|
|
Doc, SDoc, ParDoc, Img, SImg, ParImg, Rel: string;
|
|
PDoc, PImg: Integer;
|
|
begin
|
|
Doc := ASrc;
|
|
Img := ADst;
|
|
repeat
|
|
PDoc := Pos('\', Doc);
|
|
if PDoc > 0 then
|
|
begin
|
|
ParDoc := Copy(Doc, 1, PDoc);
|
|
ParDoc[Length(ParDoc)] := '/';
|
|
SDoc := SDoc + ParDoc;
|
|
Delete(Doc, 1, PDoc);
|
|
end;
|
|
PImg := Pos('\', Img);
|
|
if PImg > 0 then
|
|
begin
|
|
ParImg := Copy(Img, 1, PImg);
|
|
ParImg[Length(ParImg)] := '/';
|
|
SImg := SImg + ParImg;
|
|
Delete(Img, 1, PImg);
|
|
end;
|
|
if (PDoc > 0) and (PImg > 0) and (SDoc <> SImg) then
|
|
Rel := '../' + Rel + ParImg;
|
|
if (PDoc = 0) and (PImg <> 0) then
|
|
begin
|
|
Rel := Rel + ParImg + Img;
|
|
if Pos(':', Rel) > 0 then
|
|
Rel := '';
|
|
Result := Rel;
|
|
Exit;
|
|
end;
|
|
if (PDoc > 0) and (PImg = 0) then
|
|
begin
|
|
Rel := '../' + Rel;
|
|
end;
|
|
until (PDoc = 0) and (PImg = 0);
|
|
Rel := Rel + ExtractFileName(Img);
|
|
if Pos(':', Rel) > 0 then
|
|
Rel := '';
|
|
Result := Rel;
|
|
end;
|
|
|
|
procedure GetHTMLAnchors(const AFile: string; AList: TStringList);
|
|
var
|
|
S, SA: string;
|
|
P1, P2: Integer;
|
|
begin
|
|
S := LoadString(AFile);
|
|
P1 := 1;
|
|
repeat
|
|
P1 := PosText('<a name="', S, P1);
|
|
if P1 <> 0 then
|
|
begin
|
|
P2 := PosText('"', S, P1 + 9);
|
|
if P2 <> 0 then
|
|
begin
|
|
SA := Copy(S, P1 + 9, P2 - P1 - 9);
|
|
AList.Add(SA);
|
|
P1 := P2;
|
|
end
|
|
else
|
|
P1 := 0;
|
|
end;
|
|
until P1 = 0;
|
|
end;
|
|
|
|
function UppercaseHTMLTags(const AText: string): string;
|
|
var
|
|
P, P2: Integer;
|
|
begin
|
|
Result := '';
|
|
P2 := 1;
|
|
repeat
|
|
P := PosStr('<', AText, P2);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + Copy(AText, P2, P - P2);
|
|
P2 := P;
|
|
if Copy(AText, P, 4) = '<!--' then
|
|
begin
|
|
P := PosStr('-->', AText, P);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + Copy(AText, P2, P + 3 - P2);
|
|
P2 := P + 3;
|
|
end
|
|
else
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end
|
|
else
|
|
begin
|
|
P := PosStr('>', AText, P);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + UpperCase(Copy(AText, P2, P - P2 + 1));
|
|
P2 := P + 1;
|
|
end
|
|
else
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end;
|
|
until P = 0;
|
|
end;
|
|
|
|
function LowercaseHTMLTags(const AText: string): string;
|
|
var
|
|
P, P2: Integer;
|
|
begin
|
|
Result := '';
|
|
P2 := 1;
|
|
repeat
|
|
P := PosStr('<', AText, P2);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + Copy(AText, P2, P - P2);
|
|
P2 := P;
|
|
// now check for comments
|
|
if Copy(AText, P, 4) = '<!--' then
|
|
begin
|
|
P := PosStr('-->', AText, P);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + Copy(AText, P2, P + 3 - P2);
|
|
P2 := P + 3;
|
|
end
|
|
else
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end
|
|
else
|
|
begin
|
|
P := PosStr('>', AText, P);
|
|
if P > 0 then
|
|
begin
|
|
Result := Result + LowerCase(Copy(AText, P2, P - P2 + 1));
|
|
P2 := P + 1;
|
|
end
|
|
else
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Result := Result + Copy(AText, P2, Length(AText));
|
|
end;
|
|
until P = 0;
|
|
end;
|
|
|
|
function HexToColor(const AText: string): TColor;
|
|
begin
|
|
Result := clBlack;
|
|
if Length(AText) <> 7 then
|
|
Exit;
|
|
if AText[1] <> '#' then
|
|
Exit;
|
|
try
|
|
Result := StringToColor('$' + Copy(AText, 6, 2) + Copy(AText, 4, 2) + Copy(AText, 2, 2));
|
|
except
|
|
Result := clBlack;
|
|
end;
|
|
end;
|
|
|
|
function PosEscaped(Start: Integer; const SourceText, FindText: string; EscapeChar: Char): Integer;
|
|
begin
|
|
Result := PosText(FindText, SourceText, Start);
|
|
if Result = 0 then
|
|
Exit;
|
|
if Result = 1 then
|
|
Exit;
|
|
if SourceText[Result - 1] <> EscapeChar then
|
|
Exit;
|
|
repeat
|
|
Result := PosText(FindText, SourceText, Result + 1);
|
|
if Result = 0 then
|
|
Exit;
|
|
until SourceText[Result - 1] <> EscapeChar;
|
|
end;
|
|
|
|
function DeleteEscaped(const SourceText: string; EscapeChar: Char): string;
|
|
var
|
|
I: Integer;
|
|
RealLen: Integer;
|
|
begin
|
|
RealLen := 0;
|
|
SetLength(Result, Length(SourceText));
|
|
for I := 1 to Length(SourceText) do
|
|
if SourceText[I] <> EscapeChar then
|
|
begin
|
|
Inc(RealLen);
|
|
Result[RealLen] := SourceText[I];
|
|
end;
|
|
SetLength(Result, RealLen);
|
|
end;
|
|
|
|
procedure RecurseDirFiles(const ADir: string; var AFileList: TStringList);
|
|
var
|
|
SR: TSearchRec;
|
|
FileAttrs: Integer;
|
|
begin
|
|
FileAttrs := faAnyFile or faDirectory;
|
|
if FindFirst(ADir + PathDelim + AllFilePattern, FileAttrs, SR) = 0 then
|
|
while FindNext(SR) = 0 do
|
|
if (SR.Attr and faDirectory) <> 0 then
|
|
begin
|
|
if (SR.Name <> '.') and (SR.Name <> '..') then
|
|
RecurseDirFiles(ADir + PathDelim + SR.Name, AFileList);
|
|
end
|
|
else
|
|
AFileList.Add(ADir + PathDelim + SR.Name);
|
|
FindClose(SR);
|
|
end;
|
|
|
|
procedure RecurseDirProgs(const ADir: string; var AFileList: TStringList);
|
|
var
|
|
SR: TSearchRec;
|
|
FileAttrs: Integer;
|
|
E: string;
|
|
{$IFDEF UNIX}
|
|
ST: TStatBuf;
|
|
{$ENDIF UNIX}
|
|
begin
|
|
FileAttrs := faAnyFile or faDirectory;
|
|
if FindFirst(ADir + PathDelim + AllFilePattern, FileAttrs, SR) = 0 then
|
|
while FindNext(SR) = 0 do
|
|
begin
|
|
if (SR.Attr and faDirectory) <> 0 then
|
|
begin
|
|
if (SR.Name <> '.') and (SR.Name <> '..') then
|
|
RecurseDirProgs(ADir + PathDelim + SR.Name, AFileList);
|
|
end
|
|
{$IFDEF MSWINDOWS}
|
|
else
|
|
begin
|
|
E := LowerCase(ExtractFileExt(SR.Name));
|
|
if E = '.exe' then
|
|
AFileList.Add(ADir + PathDelim + SR.Name);
|
|
end;
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF UNIX}
|
|
else
|
|
begin
|
|
if stat(PChar(ADir + PathDelim + SR.Name), ST) = 0 then
|
|
begin
|
|
if ST.st_mode and (S_IXUSR or S_IXGRP or S_IXOTH) <> 0 then
|
|
AFileList.Add(ADir + PathDelim + SR.Name);
|
|
end;
|
|
end;
|
|
{$ENDIF UNIX}
|
|
end;
|
|
FindClose(SR);
|
|
end;
|
|
|
|
procedure LoadResourceFile(AFile: string; MemStream: TMemoryStream);
|
|
var
|
|
HResInfo: HRSRC;
|
|
HGlobal: THandle;
|
|
Buffer, GoodType: PChar;
|
|
Ext: string;
|
|
begin
|
|
Ext := UpperCase(ExtractFileExt(AFile));
|
|
Ext := Copy(Ext, 2, Length(Ext));
|
|
if Ext = 'HTM' then
|
|
Ext := 'HTML';
|
|
GoodType := PChar(Ext);
|
|
AFile := ChangeFileExt(AFile, '');
|
|
HResInfo := FindResource(HInstance, PChar(AFile), GoodType);
|
|
HGlobal := LoadResource(HInstance, HResInfo);
|
|
if HGlobal = 0 then
|
|
raise EResNotFound.CreateResFmt(@RsECannotLoadResource, [AFile]);
|
|
Buffer := LockResource(HGlobal);
|
|
MemStream.Clear;
|
|
MemStream.WriteBuffer(Buffer[0], SizeOfResource(HInstance, HResInfo));
|
|
MemStream.Seek(0, 0);
|
|
UnlockResource(HGlobal);
|
|
FreeResource(HGlobal);
|
|
end;
|
|
|
|
procedure GetNames(AText: string; AList: TStringList);
|
|
var
|
|
P: Integer;
|
|
S: string;
|
|
begin
|
|
AList.Clear;
|
|
repeat
|
|
AText := Trim(AText);
|
|
P := Pos('="', AText);
|
|
if P > 0 then
|
|
begin
|
|
S := Copy(AText, 1, P - 1);
|
|
AList.Add(S);
|
|
Delete(AText, 1, P + 1);
|
|
P := Pos('"', AText);
|
|
if P > 0 then
|
|
Delete(AText, 1, P);
|
|
end;
|
|
until P = 0;
|
|
end;
|
|
|
|
function NameValuesToXML(const AText: string): string;
|
|
var
|
|
AList: TStringList;
|
|
I, C: Integer;
|
|
IName, IValue, Xml: string;
|
|
begin
|
|
Result := '';
|
|
if AText = '' then
|
|
Exit;
|
|
AList := TStringList.Create;
|
|
GetNames(AText, AList);
|
|
C := AList.Count;
|
|
if C = 0 then
|
|
begin
|
|
AList.Free;
|
|
Exit
|
|
end;
|
|
Xml := '<accountdata>' + Cr;
|
|
for I := 0 to C - 1 do
|
|
begin
|
|
IName := AList[I];
|
|
IValue := GetValue(AText, IName);
|
|
IValue := StringReplace(IValue, '~~', Cr, [rfReplaceAll]);
|
|
Xml := Xml + '<' + IName + '>' + Cr;
|
|
Xml := Xml + ' ' + IValue + Cr;
|
|
Xml := Xml + '</' + IName + '>' + Cr;
|
|
end;
|
|
Xml := Xml + '</accountdata>' + Cr;
|
|
AList.Free;
|
|
Result := Xml;
|
|
end;
|
|
|
|
function LastPosChar(const FindChar: Char; SourceString: string): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
I := Length(SourceString);
|
|
while (I > 0) and (SourceString[I] <> FindChar) do
|
|
Dec(I);
|
|
Result := I;
|
|
end;
|
|
|
|
function PosStr(const FindString, SourceString: string; StartPos: Integer): Integer;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBX
|
|
PUSH EDX
|
|
TEST EAX,EAX
|
|
JE @@qt
|
|
TEST EDX,EDX
|
|
JE @@qt0
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
MOV EAX,[EAX-4]
|
|
MOV EDX,[EDX-4]
|
|
DEC EAX
|
|
SUB EDX,EAX
|
|
DEC ECX
|
|
SUB EDX,ECX
|
|
JNG @@qt0
|
|
MOV EBX,EAX
|
|
XCHG EAX,EDX
|
|
NOP
|
|
ADD EDI,ECX
|
|
MOV ECX,EAX
|
|
MOV AL,BYTE PTR [ESI]
|
|
@@lp1: CMP AL,BYTE PTR [EDI]
|
|
JE @@uu
|
|
@@fr: INC EDI
|
|
DEC ECX
|
|
JNZ @@lp1
|
|
@@qt0: XOR EAX,EAX
|
|
JMP @@qt
|
|
@@ms: MOV AL,BYTE PTR [ESI]
|
|
MOV EBX,EDX
|
|
JMP @@fr
|
|
@@uu: TEST EDX,EDX
|
|
JE @@fd
|
|
@@lp2: MOV AL,BYTE PTR [ESI+EBX]
|
|
XOR AL,BYTE PTR [EDI+EBX]
|
|
JNE @@ms
|
|
DEC EBX
|
|
JNE @@lp2
|
|
@@fd: LEA EAX,[EDI+1]
|
|
SUB EAX,[ESP]
|
|
@@qt: POP ECX
|
|
POP EBX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
function PosText(const FindString, SourceString: string; StartPos: Integer): Integer;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBX
|
|
NOP
|
|
TEST EAX,EAX
|
|
JE @@qt
|
|
TEST EDX,EDX
|
|
JE @@qt0
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
PUSH EDX
|
|
MOV EAX,[EAX-4]
|
|
MOV EDX,[EDX-4]
|
|
DEC EAX
|
|
SUB EDX,EAX
|
|
DEC ECX
|
|
PUSH EAX
|
|
SUB EDX,ECX
|
|
JNG @@qtx
|
|
ADD EDI,ECX
|
|
MOV ECX,EDX
|
|
MOV EDX,EAX
|
|
MOVZX EBX,BYTE PTR [ESI]
|
|
MOV AL,BYTE PTR [EBX+ToUpperChars]
|
|
@@lp1: MOVZX EBX,BYTE PTR [EDI]
|
|
CMP AL,BYTE PTR [EBX+ToUpperChars]
|
|
JE @@uu
|
|
@@fr: INC EDI
|
|
DEC ECX
|
|
JNE @@lp1
|
|
@@qtx: ADD ESP,$08
|
|
@@qt0: XOR EAX,EAX
|
|
JMP @@qt
|
|
@@ms: MOVZX EBX,BYTE PTR [ESI]
|
|
MOV AL,BYTE PTR [EBX+ToUpperChars]
|
|
MOV EDX,[ESP]
|
|
JMP @@fr
|
|
NOP
|
|
@@uu: TEST EDX,EDX
|
|
JE @@fd
|
|
@@lp2: MOV BL,BYTE PTR [ESI+EDX]
|
|
MOV AH,BYTE PTR [EDI+EDX]
|
|
CMP BL,AH
|
|
JE @@eq
|
|
MOV AL,BYTE PTR [EBX+ToUpperChars]
|
|
MOVZX EBX,AH
|
|
XOR AL,BYTE PTR [EBX+ToUpperChars]
|
|
JNE @@ms
|
|
@@eq: DEC EDX
|
|
JNZ @@lp2
|
|
@@fd: LEA EAX,[EDI+1]
|
|
POP ECX
|
|
SUB EAX,[ESP]
|
|
POP ECX
|
|
@@qt: POP EBX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
function GetBoolValue(const AText, AName: string): Boolean;
|
|
begin
|
|
Result := LowerCase(GetValue(AText, AName)) = 'yes';
|
|
end;
|
|
|
|
procedure ListSelect(Src, Dst: TStringList; const AKey, AValue: string);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Dst.Clear;
|
|
for I := 0 to Src.Count - 1 do
|
|
begin
|
|
if GetValue(Src[I], AKey) = AValue then
|
|
Dst.Add(Src[I]);
|
|
end;
|
|
end;
|
|
|
|
procedure ListFilter(Src: TStringList; const AKey, AValue: string);
|
|
var
|
|
I: Integer;
|
|
Dst: TStringList;
|
|
begin
|
|
Dst := TStringList.Create;
|
|
for I := 0 to Src.Count - 1 do
|
|
begin
|
|
if GetValue(Src[I], AKey) = AValue then
|
|
Dst.Add(Src[I]);
|
|
end;
|
|
Src.Assign(Dst);
|
|
Dst.Free;
|
|
end;
|
|
|
|
procedure ListOrderBy(Src: TStringList; const AKey: string; Numeric: Boolean);
|
|
var
|
|
I, Index: Integer;
|
|
Lit, Dst: TStringList;
|
|
S: string;
|
|
IValue: Integer;
|
|
begin
|
|
if Src.Count < 2 then
|
|
Exit; // nothing to sort
|
|
Lit := TStringList.Create;
|
|
Dst := TStringList.Create;
|
|
for I := 0 to Src.Count - 1 do
|
|
begin
|
|
S := GetValue(Src[I], AKey);
|
|
if Numeric then
|
|
try
|
|
IValue := StrToInt(S);
|
|
// format to 5 decimal places for correct string sorting
|
|
// e.g. 5 becomes 00005
|
|
S := Format('%5.5d', [IValue]);
|
|
except
|
|
// just use the unformatted value
|
|
end;
|
|
Lit.AddObject(S, TObject(I));
|
|
end;
|
|
Lit.Sort;
|
|
for I := 0 to Src.Count - 1 do
|
|
begin
|
|
Index := Integer(Lit.Objects[I]);
|
|
Dst.Add(Src[Index]);
|
|
end;
|
|
Lit.Free;
|
|
Src.Assign(Dst);
|
|
Dst.Free;
|
|
end;
|
|
|
|
// converts a csv list to a tagged string list
|
|
|
|
procedure CSVToTags(Src, Dst: TStringList);
|
|
var
|
|
I, FI, FC: Integer;
|
|
Names: TStringList;
|
|
Rec: TStringList;
|
|
S: string;
|
|
begin
|
|
Dst.Clear;
|
|
if Src.Count < 2 then
|
|
Exit;
|
|
Names := TStringList.Create;
|
|
Rec := TStringList.Create;
|
|
try
|
|
Names.CommaText := Src[0];
|
|
FC := Names.Count;
|
|
if FC > 0 then
|
|
for I := 1 to Src.Count - 1 do
|
|
begin
|
|
Rec.CommaText := Src[I];
|
|
S := '';
|
|
for FI := 0 to FC - 1 do
|
|
S := S + Names[FI] + '="' + Rec[FI] + '" ';
|
|
Dst.Add(S);
|
|
end;
|
|
finally
|
|
Rec.Free;
|
|
Names.Free;
|
|
end;
|
|
end;
|
|
|
|
// converts a tagged string list to a csv list
|
|
// only fieldnames from the first record are scanned ib the other records
|
|
|
|
procedure TagsToCSV(Src, Dst: TStringList);
|
|
var
|
|
I, FI, FC: Integer;
|
|
Names: TStringList;
|
|
Rec: TStringList;
|
|
S: string;
|
|
begin
|
|
Dst.Clear;
|
|
if Src.Count < 1 then
|
|
Exit;
|
|
Names := TStringList.Create;
|
|
Rec := TStringList.Create;
|
|
try
|
|
GetNames(Src[0], Names);
|
|
FC := Names.Count;
|
|
if FC > 0 then
|
|
begin
|
|
Dst.Add(Names.CommaText);
|
|
for I := 0 to Src.Count - 1 do
|
|
begin
|
|
S := '';
|
|
Rec.Clear;
|
|
for FI := 0 to FC - 1 do
|
|
Rec.Add(GetValue(Src[I], Names[FI]));
|
|
Dst.Add(Rec.CommaText);
|
|
end;
|
|
end;
|
|
finally
|
|
Rec.Free;
|
|
Names.Free;
|
|
end;
|
|
end;
|
|
|
|
function B64Encode;
|
|
var
|
|
I: Integer;
|
|
InBuf: array [0..2] of Byte;
|
|
OutBuf: array [0..3] of Char;
|
|
begin
|
|
SetLength(Result, ((Length(S) + 2) div 3) * 4);
|
|
for I := 1 to ((Length(S) + 2) div 3) do
|
|
begin
|
|
if Length(S) < (I * 3) then
|
|
Move(S[(I - 1) * 3 + 1], InBuf, Length(S) - (I - 1) * 3)
|
|
else
|
|
Move(S[(I - 1) * 3 + 1], InBuf, 3);
|
|
OutBuf[0] := B64Table[((InBuf[0] and $FC) shr 2) + 1];
|
|
OutBuf[1] := B64Table[(((InBuf[0] and $03) shl 4) or ((InBuf[1] and $F0) shr 4)) + 1];
|
|
OutBuf[2] := B64Table[(((InBuf[1] and $0F) shl 2) or ((InBuf[2] and $C0) shr 6)) + 1];
|
|
OutBuf[3] := B64Table[(InBuf[2] and $3F) + 1];
|
|
Move(OutBuf, Result[(I - 1) * 4 + 1], 4);
|
|
end;
|
|
if (Length(S) mod 3) = 1 then
|
|
begin
|
|
Result[Length(Result) - 1] := '=';
|
|
Result[Length(Result)] := '=';
|
|
end
|
|
else
|
|
if (Length(S) mod 3) = 2 then
|
|
Result[Length(Result)] := '=';
|
|
end;
|
|
|
|
function B64Decode(const S: string): string;
|
|
var
|
|
I: Integer;
|
|
InBuf: array [0..3] of Byte;
|
|
OutBuf: array [0..2] of Byte;
|
|
RetValue: string;
|
|
begin
|
|
if ((Length(S) mod 4) <> 0) or (S = '') then
|
|
raise EJVCLException.CreateRes(@RsEIncorrectStringFormat);
|
|
|
|
SetLength(RetValue, ((Length(S) div 4) - 1) * 3);
|
|
for I := 1 to ((Length(S) div 4) - 1) do
|
|
begin
|
|
Move(S[(I - 1) * 4 + 1], InBuf, 4);
|
|
if (InBuf[0] > 64) and (InBuf[0] < 91) then
|
|
Dec(InBuf[0], 65)
|
|
else
|
|
if (InBuf[0] > 96) and (InBuf[0] < 123) then
|
|
Dec(InBuf[0], 71)
|
|
else
|
|
if (InBuf[0] > 47) and (InBuf[0] < 58) then
|
|
Inc(InBuf[0], 4)
|
|
else
|
|
if InBuf[0] = 43 then
|
|
InBuf[0] := 62
|
|
else
|
|
InBuf[0] := 63;
|
|
if (InBuf[1] > 64) and (InBuf[1] < 91) then
|
|
Dec(InBuf[1], 65)
|
|
else
|
|
if (InBuf[1] > 96) and (InBuf[1] < 123) then
|
|
Dec(InBuf[1], 71)
|
|
else
|
|
if (InBuf[1] > 47) and (InBuf[1] < 58) then
|
|
Inc(InBuf[1], 4)
|
|
else
|
|
if InBuf[1] = 43 then
|
|
InBuf[1] := 62
|
|
else
|
|
InBuf[1] := 63;
|
|
if (InBuf[2] > 64) and (InBuf[2] < 91) then
|
|
Dec(InBuf[2], 65)
|
|
else
|
|
if (InBuf[2] > 96) and (InBuf[2] < 123) then
|
|
Dec(InBuf[2], 71)
|
|
else
|
|
if (InBuf[2] > 47) and (InBuf[2] < 58) then
|
|
Inc(InBuf[2], 4)
|
|
else
|
|
if InBuf[2] = 43 then
|
|
InBuf[2] := 62
|
|
else
|
|
InBuf[2] := 63;
|
|
if (InBuf[3] > 64) and (InBuf[3] < 91) then
|
|
Dec(InBuf[3], 65)
|
|
else
|
|
if (InBuf[3] > 96) and (InBuf[3] < 123) then
|
|
Dec(InBuf[3], 71)
|
|
else
|
|
if (InBuf[3] > 47) and (InBuf[3] < 58) then
|
|
Inc(InBuf[3], 4)
|
|
else
|
|
if InBuf[3] = 43 then
|
|
InBuf[3] := 62
|
|
else
|
|
InBuf[3] := 63;
|
|
OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $03);
|
|
OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $0F);
|
|
OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
|
|
Move(OutBuf, RetValue[(I - 1) * 3 + 1], 3);
|
|
end;
|
|
if S <> '' then
|
|
begin
|
|
Move(S[Length(S) - 3], InBuf, 4);
|
|
if InBuf[2] = 61 then
|
|
begin
|
|
if (InBuf[0] > 64) and (InBuf[0] < 91) then
|
|
Dec(InBuf[0], 65)
|
|
else
|
|
if (InBuf[0] > 96) and (InBuf[0] < 123) then
|
|
Dec(InBuf[0], 71)
|
|
else
|
|
if (InBuf[0] > 47) and (InBuf[0] < 58) then
|
|
Inc(InBuf[0], 4)
|
|
else
|
|
if InBuf[0] = 43 then
|
|
InBuf[0] := 62
|
|
else
|
|
InBuf[0] := 63;
|
|
if (InBuf[1] > 64) and (InBuf[1] < 91) then
|
|
Dec(InBuf[1], 65)
|
|
else
|
|
if (InBuf[1] > 96) and (InBuf[1] < 123) then
|
|
Dec(InBuf[1], 71)
|
|
else
|
|
if (InBuf[1] > 47) and (InBuf[1] < 58) then
|
|
Inc(InBuf[1], 4)
|
|
else
|
|
if InBuf[1] = 43 then
|
|
InBuf[1] := 62
|
|
else
|
|
InBuf[1] := 63;
|
|
OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $03);
|
|
RetValue := RetValue + Char(OutBuf[0]);
|
|
end
|
|
else
|
|
if InBuf[3] = 61 then
|
|
begin
|
|
if (InBuf[0] > 64) and (InBuf[0] < 91) then
|
|
Dec(InBuf[0], 65)
|
|
else
|
|
if (InBuf[0] > 96) and (InBuf[0] < 123) then
|
|
Dec(InBuf[0], 71)
|
|
else
|
|
if (InBuf[0] > 47) and (InBuf[0] < 58) then
|
|
Inc(InBuf[0], 4)
|
|
else
|
|
if InBuf[0] = 43 then
|
|
InBuf[0] := 62
|
|
else
|
|
InBuf[0] := 63;
|
|
if (InBuf[1] > 64) and (InBuf[1] < 91) then
|
|
Dec(InBuf[1], 65)
|
|
else
|
|
if (InBuf[1] > 96) and (InBuf[1] < 123) then
|
|
Dec(InBuf[1], 71)
|
|
else
|
|
if (InBuf[1] > 47) and (InBuf[1] < 58) then
|
|
Inc(InBuf[1], 4)
|
|
else
|
|
if InBuf[1] = 43 then
|
|
InBuf[1] := 62
|
|
else
|
|
InBuf[1] := 63;
|
|
if (InBuf[2] > 64) and (InBuf[2] < 91) then
|
|
Dec(InBuf[2], 65)
|
|
else
|
|
if (InBuf[2] > 96) and (InBuf[2] < 123) then
|
|
Dec(InBuf[2], 71)
|
|
else
|
|
if (InBuf[2] > 47) and (InBuf[2] < 58) then
|
|
Inc(InBuf[2], 4)
|
|
else
|
|
if InBuf[2] = 43 then
|
|
InBuf[2] := 62
|
|
else
|
|
InBuf[2] := 63;
|
|
OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $03);
|
|
OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $0F);
|
|
RetValue := RetValue + Char(OutBuf[0]) + Char(OutBuf[1]);
|
|
end
|
|
else
|
|
begin
|
|
if (InBuf[0] > 64) and (InBuf[0] < 91) then
|
|
Dec(InBuf[0], 65)
|
|
else
|
|
if (InBuf[0] > 96) and (InBuf[0] < 123) then
|
|
Dec(InBuf[0], 71)
|
|
else
|
|
if (InBuf[0] > 47) and (InBuf[0] < 58) then
|
|
Inc(InBuf[0], 4)
|
|
else
|
|
if InBuf[0] = 43 then
|
|
InBuf[0] := 62
|
|
else
|
|
InBuf[0] := 63;
|
|
if (InBuf[1] > 64) and (InBuf[1] < 91) then
|
|
Dec(InBuf[1], 65)
|
|
else
|
|
if (InBuf[1] > 96) and (InBuf[1] < 123) then
|
|
Dec(InBuf[1], 71)
|
|
else
|
|
if (InBuf[1] > 47) and (InBuf[1] < 58) then
|
|
Inc(InBuf[1], 4)
|
|
else
|
|
if InBuf[1] = 43 then
|
|
InBuf[1] := 62
|
|
else
|
|
InBuf[1] := 63;
|
|
if (InBuf[2] > 64) and (InBuf[2] < 91) then
|
|
Dec(InBuf[2], 65)
|
|
else
|
|
if (InBuf[2] > 96) and (InBuf[2] < 123) then
|
|
Dec(InBuf[2], 71)
|
|
else
|
|
if (InBuf[2] > 47) and (InBuf[2] < 58) then
|
|
Inc(InBuf[2], 4)
|
|
else
|
|
if InBuf[2] = 43 then
|
|
InBuf[2] := 62
|
|
else
|
|
InBuf[2] := 63;
|
|
if (InBuf[3] > 64) and (InBuf[3] < 91) then
|
|
Dec(InBuf[3], 65)
|
|
else
|
|
if (InBuf[3] > 96) and (InBuf[3] < 123) then
|
|
Dec(InBuf[3], 71)
|
|
else
|
|
if (InBuf[3] > 47) and (InBuf[3] < 58) then
|
|
Inc(InBuf[3], 4)
|
|
else
|
|
if InBuf[3] = 43 then
|
|
InBuf[3] := 62
|
|
else
|
|
InBuf[3] := 63;
|
|
OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $03);
|
|
OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $0F);
|
|
OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
|
|
RetValue := RetValue + Char(OutBuf[0]) + Char(OutBuf[1]) + Char(OutBuf[2]);
|
|
end;
|
|
end;
|
|
Result := RetValue;
|
|
end;
|
|
|
|
{*******************************************************
|
|
* Standard Encryption algorithm - Copied from Borland *
|
|
*******************************************************}
|
|
|
|
function Encrypt(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 1 to Length(InString) do
|
|
begin
|
|
Result := Result + Char(Byte(InString[I]) xor (StartKey shr 8));
|
|
StartKey := (Byte(Result[I]) + StartKey) * MultKey + AddKey;
|
|
end;
|
|
end;
|
|
{*******************************************************
|
|
* Standard Decryption algorithm - Copied from Borland *
|
|
*******************************************************}
|
|
|
|
function Decrypt(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 1 to Length(InString) do
|
|
begin
|
|
Result := Result + Char(Byte(InString[I]) xor (StartKey shr 8));
|
|
StartKey := (Byte(InString[I]) + StartKey) * MultKey + AddKey;
|
|
end;
|
|
end;
|
|
|
|
function EncryptB64(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
begin
|
|
Result := B64Encode(Encrypt(InString, StartKey, MultKey, AddKey));
|
|
end;
|
|
|
|
function DecryptB64(const InString: string; StartKey, MultKey, AddKey: Integer): string;
|
|
begin
|
|
Result := Decrypt(B64Decode(InString), StartKey, MultKey, AddKey);
|
|
end;
|
|
|
|
function Hash(const AText: string): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := 0;
|
|
if AText = '' then
|
|
Exit;
|
|
Result := Ord(AText[1]);
|
|
for I := 2 to Length(AText) do
|
|
Result := (Result * Ord(AText[I])) xor Result;
|
|
end;
|
|
|
|
{replace any <,> etc by < >}
|
|
|
|
function XMLSafe(const AText: string): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 1 to Length(AText) do
|
|
if AText[I] = '<' then
|
|
Result := Result + '<'
|
|
else
|
|
if AText[I] = '>' then
|
|
Result := Result + '>'
|
|
else
|
|
if AText[I] = '&' then
|
|
Result := Result + '&'
|
|
else
|
|
if (Ord(AText[I]) >= 32) and (Ord(AText[I]) < 128) then
|
|
Result := Result + AText[I]
|
|
else
|
|
if Ord(AText[I]) > 127 then
|
|
Result := Result + '&#' + IntToStr(Ord(AText[I])) + ';'
|
|
else
|
|
Result := Result + ' ';
|
|
end;
|
|
|
|
function FirstOfSet(const AText: string): string;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
Result := Trim(AText);
|
|
if Result = '' then
|
|
Exit;
|
|
if Result[1] = '"' then
|
|
begin
|
|
P := PosStr('"', Result, 2);
|
|
Result := Copy(Result, 2, P - 2);
|
|
end
|
|
else
|
|
begin
|
|
P := Pos(' ', Result);
|
|
Result := Copy(Result, 1, P - 1);
|
|
end;
|
|
end;
|
|
|
|
function LastOfSet(const AText: string): string;
|
|
var
|
|
C: Integer;
|
|
begin
|
|
Result := Trim(AText);
|
|
if Result = '' then
|
|
Exit;
|
|
C := Length(Result);
|
|
if Result[C] = '"' then
|
|
begin
|
|
while (C > 1) and (Result[C - 1] <> '"') do
|
|
Dec(C);
|
|
Result := Copy(Result, C, Length(Result) - C);
|
|
end
|
|
else
|
|
begin
|
|
while (C > 1) and (Result[C - 1] <> ' ') do
|
|
Dec(C);
|
|
Result := Copy(Result, C, Length(Result));
|
|
end;
|
|
end;
|
|
|
|
function CountOfSet(const AText: string): Integer;
|
|
var
|
|
Lit: TStringList;
|
|
begin
|
|
Lit := TStringList.Create;
|
|
SplitSet(AText, Lit);
|
|
Result := Lit.Count;
|
|
Lit.Free;
|
|
end;
|
|
|
|
function SetRotateRight(const AText: string): string;
|
|
var
|
|
Lit: TStringList;
|
|
C: Integer;
|
|
begin
|
|
Lit := TStringList.Create;
|
|
SplitSet(AText, Lit);
|
|
C := Lit.Count;
|
|
if C > 0 then
|
|
begin
|
|
Lit.Move(C - 1, 0);
|
|
Result := JoinSet(Lit);
|
|
end
|
|
else
|
|
Result := '';
|
|
Lit.Free;
|
|
end;
|
|
|
|
function SetRotateLeft(const AText: string): string;
|
|
var
|
|
Lit: TStringList;
|
|
C: Integer;
|
|
begin
|
|
Lit := TStringList.Create;
|
|
SplitSet(AText, Lit);
|
|
C := Lit.Count;
|
|
if C > 0 then
|
|
begin
|
|
Lit.Move(0, C - 1);
|
|
Result := JoinSet(Lit);
|
|
end
|
|
else
|
|
Result := '';
|
|
Lit.Free;
|
|
end;
|
|
|
|
procedure SplitSet(AText: string; AList: TStringList);
|
|
var
|
|
P: Integer;
|
|
begin
|
|
AList.Clear;
|
|
if AText = '' then
|
|
Exit;
|
|
AText := Trim(AText);
|
|
while AText <> '' do
|
|
begin
|
|
if AText[1] = '"' then
|
|
begin
|
|
Delete(AText, 1, 1);
|
|
P := Pos('"', AText);
|
|
if P <> 0 then
|
|
begin
|
|
AList.Add(Copy(AText, 1, P - 1));
|
|
Delete(AText, 1, P);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
P := Pos(' ', AText);
|
|
if P = 0 then
|
|
begin
|
|
AList.Add(AText);
|
|
AText := '';
|
|
end
|
|
else
|
|
begin
|
|
AList.Add(Copy(AText, 1, P - 1));
|
|
Delete(AText, 1, P);
|
|
end;
|
|
end;
|
|
AText := Trim(AText);
|
|
end;
|
|
end;
|
|
|
|
function JoinSet(AList: TStringList): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 0 to AList.Count - 1 do
|
|
Result := Result + AList[I] + ' ';
|
|
Delete(Result, Length(Result), 1);
|
|
end;
|
|
|
|
function SetPick(const AText: string; AIndex: Integer): string;
|
|
var
|
|
Lit: TStringList;
|
|
C: Integer;
|
|
begin
|
|
Lit := TStringList.Create;
|
|
SplitSet(AText, Lit);
|
|
C := Lit.Count;
|
|
if (C > 0) and (AIndex < C) then
|
|
Result := Lit[AIndex]
|
|
else
|
|
Result := '';
|
|
Lit.Free;
|
|
end;
|
|
|
|
function SetSort(const AText: string): string;
|
|
var
|
|
Lit: TStringList;
|
|
begin
|
|
Lit := TStringList.Create;
|
|
SplitSet(AText, Lit);
|
|
if Lit.Count > 0 then
|
|
begin
|
|
Lit.Sort;
|
|
Result := JoinSet(Lit);
|
|
end
|
|
else
|
|
Result := '';
|
|
Lit.Free;
|
|
end;
|
|
|
|
function SetUnion(const Set1, Set2: string): string;
|
|
var
|
|
Lit1, Lit2, Lit3: TStringList;
|
|
I, C: Integer;
|
|
begin
|
|
Lit1 := TStringList.Create;
|
|
Lit2 := TStringList.Create;
|
|
Lit3 := TStringList.Create;
|
|
SplitSet(Set1, Lit1);
|
|
SplitSet(Set2, Lit2);
|
|
C := Lit2.Count;
|
|
if C <> 0 then
|
|
begin
|
|
Lit2.Addstrings(Lit1);
|
|
for I := 0 to Lit2.Count - 1 do
|
|
if Lit3.IndexOf(Lit2[I]) = -1 then
|
|
Lit3.Add(Lit2[I]);
|
|
Result := JoinSet(Lit3);
|
|
end
|
|
else
|
|
begin
|
|
Result := JoinSet(Lit1);
|
|
end;
|
|
Lit1.Free;
|
|
Lit2.Free;
|
|
Lit3.Free;
|
|
end;
|
|
|
|
function SetIntersect(const Set1, Set2: string): string;
|
|
var
|
|
Lit1, Lit2, Lit3: TStringList;
|
|
I: Integer;
|
|
begin
|
|
Lit1 := TStringList.Create;
|
|
Lit2 := TStringList.Create;
|
|
Lit3 := TStringList.Create;
|
|
SplitSet(Set1, Lit1);
|
|
SplitSet(Set2, Lit2);
|
|
if Lit2.Count <> 0 then
|
|
begin
|
|
for I := 0 to Lit2.Count - 1 do
|
|
if Lit1.IndexOf(Lit2[I]) <> -1 then
|
|
Lit3.Add(Lit2[I]);
|
|
Result := JoinSet(Lit3);
|
|
end
|
|
else
|
|
Result := '';
|
|
Lit1.Free;
|
|
Lit2.Free;
|
|
Lit3.Free;
|
|
end;
|
|
|
|
function SetExclude(const Set1, Set2: string): string;
|
|
var
|
|
Lit1, Lit2: TStringList;
|
|
I, Index: Integer;
|
|
begin
|
|
Lit1 := TStringList.Create;
|
|
Lit2 := TStringList.Create;
|
|
SplitSet(Set1, Lit1);
|
|
SplitSet(Set2, Lit2);
|
|
if Lit2.Count <> 0 then
|
|
begin
|
|
for I := 0 to Lit2.Count - 1 do
|
|
begin
|
|
Index := Lit1.IndexOf(Lit2[I]);
|
|
if Index <> -1 then
|
|
Lit1.Delete(Index);
|
|
end;
|
|
Result := JoinSet(Lit1);
|
|
end
|
|
else
|
|
Result := JoinSet(Lit1);
|
|
Lit1.Free;
|
|
Lit2.Free;
|
|
end;
|
|
|
|
// This function converts a string into a RFC 1630 compliant URL
|
|
|
|
function URLEncode(const Value: string): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := '';
|
|
for I := 1 to Length(Value) do
|
|
if Pos(UpperCase(Value[I]), ValidURLChars) > 0 then
|
|
Result := Result + Value[I]
|
|
else
|
|
begin
|
|
if Value[I] = ' ' then
|
|
Result := Result + '+'
|
|
else
|
|
begin
|
|
Result := Result + '%';
|
|
Result := Result + IntToHex(Byte(Value[I]), 2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function URLDecode(const Value: string): string;
|
|
const
|
|
HexChars = '0123456789ABCDEF';
|
|
var
|
|
I: Integer;
|
|
Ch, H1, H2: Char;
|
|
Len: Integer;
|
|
begin
|
|
Result := '';
|
|
Len := Length(Value);
|
|
I := 1;
|
|
while I <= Len do
|
|
begin
|
|
Ch := Value[I];
|
|
case Ch of
|
|
'%':
|
|
begin
|
|
H1 := Value[I + 1];
|
|
H2 := Value[I + 2];
|
|
Inc(I, 2);
|
|
Result := Result + Chr(((Pos(H1, HexChars) - 1) * 16) + (Pos(H2, HexChars) - 1));
|
|
end;
|
|
'+':
|
|
Result := Result + ' ';
|
|
'&':
|
|
Result := Result + CrLf;
|
|
else
|
|
Result := Result + Ch;
|
|
end;
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
{template functions}
|
|
|
|
function ReplaceFirst(const SourceStr, FindStr, ReplaceStr: string): string;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
Result := SourceStr;
|
|
P := PosText(FindStr, SourceStr, 1);
|
|
if P <> 0 then
|
|
Result := Copy(SourceStr, 1, P - 1) + ReplaceStr + Copy(SourceStr, P + Length(FindStr), Length(SourceStr));
|
|
end;
|
|
|
|
function ReplaceLast(const SourceStr, FindStr, ReplaceStr: string): string;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
Result := SourceStr;
|
|
P := PosTextLast(FindStr, SourceStr);
|
|
if P <> 0 then
|
|
Result := Copy(SourceStr, 1, P - 1) + ReplaceStr + Copy(SourceStr, P + Length(FindStr), Length(SourceStr));
|
|
end;
|
|
|
|
// insert a block template
|
|
// the last occurance of {block:aBlockname}
|
|
// the block template is marked with {begin:aBlockname} and {end:aBlockname}
|
|
|
|
function InsertLastBlock(var SourceStr: string; BlockStr: string): Boolean;
|
|
var
|
|
// phead: Integer;
|
|
PBlock, PE, PB: Integer;
|
|
SBB, SBE, SB, SBR: string;
|
|
SBBL, SBEL: Integer;
|
|
begin
|
|
Result := False;
|
|
// phead:= PosStr('</head>',SourceStr,1);
|
|
// If phead = 0 Then Exit;
|
|
// phead:= phead + 7;
|
|
SB := '{block:' + BlockStr + '}';
|
|
// sbL:=Length(SB);
|
|
SBB := '{begin:' + BlockStr + '}';
|
|
SBBL := Length(SBB);
|
|
SBE := '{end:' + BlockStr + '}';
|
|
SBEL := Length(SBE);
|
|
PBlock := PosTextLast(SB, SourceStr);
|
|
if PBlock = 0 then
|
|
Exit;
|
|
PB := PosText(SBB, SourceStr, 1);
|
|
if PB = 0 then
|
|
Exit;
|
|
PE := PosText(SBE, SourceStr, PB);
|
|
if PE = 0 then
|
|
Exit;
|
|
PE := PE + SBEL - 1;
|
|
// now replace
|
|
SBR := Copy(SourceStr, PB + SBBL, PE - PB - SBBL - SBEL + 1);
|
|
SourceStr := Copy(SourceStr, 1, PBlock - 1) + SBR + Copy(SourceStr, PBlock, Length(SourceStr));
|
|
Result := True;
|
|
end;
|
|
|
|
// removes all {begin:somefield} to {end:somefield} from ASource
|
|
|
|
function RemoveMasterBlocks(const SourceStr: string): string;
|
|
var
|
|
S, Src: string;
|
|
PB: Integer;
|
|
PE: Integer;
|
|
PEE: Integer;
|
|
begin
|
|
S := '';
|
|
Src := SourceStr;
|
|
repeat
|
|
PB := PosText('{begin:', Src);
|
|
if PB > 0 then
|
|
begin
|
|
PE := PosText('{end:', Src, PB);
|
|
if PE > 0 then
|
|
begin
|
|
PEE := PosStr('}', Src, PE);
|
|
if PEE > 0 then
|
|
begin
|
|
S := S + Copy(Src, 1, PB - 1);
|
|
Delete(Src, 1, PEE);
|
|
end;
|
|
end;
|
|
end;
|
|
until PB = 0;
|
|
Result := S + Src;
|
|
end;
|
|
|
|
// removes all {field} entries in a template
|
|
|
|
function RemoveFields(const SourceStr: string): string;
|
|
var
|
|
Src, S: string;
|
|
PB: Integer;
|
|
PE: Integer;
|
|
begin
|
|
S := '';
|
|
Src := SourceStr;
|
|
repeat
|
|
PB := Pos('{', Src);
|
|
if PB > 0 then
|
|
begin
|
|
PE := Pos('}', Src);
|
|
if PE > 0 then
|
|
begin
|
|
S := S + Copy(Src, 1, PB - 1);
|
|
Delete(Src, 1, PE);
|
|
end;
|
|
end;
|
|
until PB = 0;
|
|
Result := S + Src;
|
|
end;
|
|
|
|
{finds the last occurance}
|
|
|
|
function PosStrLast(const FindString, SourceString: string): Integer;
|
|
var
|
|
I, L: Integer;
|
|
begin
|
|
Result := 0;
|
|
L := Length(FindString);
|
|
if L = 0 then
|
|
Exit;
|
|
I := Length(SourceString);
|
|
if I = 0 then
|
|
Exit;
|
|
I := I - L + 1;
|
|
while I > 0 do
|
|
begin
|
|
Result := PosStr(FindString, SourceString, I);
|
|
if Result > 0 then
|
|
Exit;
|
|
I := I - L;
|
|
end;
|
|
end;
|
|
|
|
{finds the last occurance}
|
|
|
|
function PosTextLast(const FindString, SourceString: string): Integer;
|
|
var
|
|
I, L: Integer;
|
|
begin
|
|
Result := 0;
|
|
L := Length(FindString);
|
|
if L = 0 then
|
|
Exit;
|
|
I := Length(SourceString);
|
|
if I = 0 then
|
|
Exit;
|
|
I := I - L + 1;
|
|
while I > 0 do
|
|
begin
|
|
Result := PosText(FindString, SourceString, I);
|
|
if Result > 0 then
|
|
Exit;
|
|
I := I - L;
|
|
end;
|
|
end;
|
|
|
|
procedure DirFiles(const ADir, AMask: string; AFileList: TStringList);
|
|
var
|
|
SR: TSearchRec;
|
|
FileAttrs: Integer;
|
|
begin
|
|
FileAttrs := faArchive + faDirectory;
|
|
if FindFirst(ADir + AMask, FileAttrs, SR) = 0 then
|
|
while FindNext(SR) = 0 do
|
|
if (SR.Attr and faArchive) <> 0 then
|
|
AFileList.Add(ADir + SR.Name);
|
|
FindClose(SR);
|
|
end;
|
|
|
|
// parse number returns the last position, starting from 1
|
|
|
|
function ParseNumber(const S: string): Integer;
|
|
var
|
|
I, E, E2, C: Integer;
|
|
begin
|
|
Result := 0;
|
|
I := 0;
|
|
C := Length(S);
|
|
if C = 0 then
|
|
Exit;
|
|
while (I + 1 <= C) and (S[I + 1] in (DigitChars + [',', '.'])) do
|
|
Inc(I);
|
|
if (I + 1 <= C) and (S[I + 1] in ['e', 'E']) then
|
|
begin
|
|
E := I;
|
|
Inc(I);
|
|
if (I + 1 <= C) and (S[I + 1] in ['+', '-']) then
|
|
Inc(I);
|
|
E2 := I;
|
|
while (I + 1 <= C) and (S[I + 1] in DigitChars) do
|
|
Inc(I);
|
|
if I = E2 then
|
|
I := E;
|
|
end;
|
|
Result := I;
|
|
end;
|
|
|
|
// parse a SQL style data string from positions 1,
|
|
// starts and ends with #
|
|
|
|
function ParseDate(const S: string): Integer;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
Result := 0;
|
|
if Length(S) < 2 then
|
|
Exit;
|
|
P := PosStr('#', S, 2);
|
|
if P <> 0 then
|
|
try
|
|
StrToDate(Copy(S, 2, P - 2));
|
|
Result := P;
|
|
except
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|