{----------------------------------------------------------------------------- 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. .... case sensitive function PosRangeText(Start: Integer; const HeadString, TailString, SourceString: string; var RangeBegin: Integer; var RangeEnd: Integer): Boolean; // finds a text range, e.g. .... 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. .... 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. .... 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. .... 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. .... 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(' 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) = '', 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) = '', 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 := '' + 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 + '' + Cr; end; Xml := Xml + '' + 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('',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.