lazarus-ccr/components/zmsql/source/jansqlstrings.pas

494 lines
14 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/NPL/NPL-1_1Final.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: janSQLStrings.pas, released March 24, 2002.
The Initial Developer of the Original Code is Jan Verhoeven
(jan1.verhoeven@wxs.nl or http://jansfreeware.com).
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
All Rights Reserved.
Contributor(s): ___________________.
Last Modified: 24-mar-2002
Current Version: 1.0
Notes: A set of string routines that are just usefull with janSQL
Known Issues:
History:
1.1 25-mar-2002
added functions for section strings
1.0 24-mar-2002 : original release
-----------------------------------------------------------------------------}
{$ifdef fpc}
{$mode delphi} {$H+}
{$endif}
unit janSQLStrings;
interface
uses
{$IFDEF UNIX} clocale, cwstring,{$ENDIF}
Classes,sysUtils{,qstrings};
function PosStr(const FindString, SourceString: string;
StartPos: Integer = 1): Integer;
function PosText(const FindString, SourceString: string;
StartPos: Integer = 1): Integer;
function Contains(const value:variant;const aset:string):boolean;
function Soundex(source : string) : integer;
procedure SaveString(aFile, aText:string);
function LoadString(aFile:string):string;
procedure ListSections(atext:string;list:TStrings);
function GetSection(atext,asection:string):string;
function Easter( nYear: Integer ): TDateTime;
function DateToSQLString(adate:TDateTime):string;
function SQLStringToDate(atext:string):TDateTime;
function Date2Year (const DT: TDateTime): Word;
function GetFirstDayOfYear (const Year: Word): TDateTime;
function StartOfWeek (const DT: TDateTime): TDateTime;
function DaysApart (const DT1, DT2: TDateTime): LongInt;
function Date2WeekNo (const DT: TDateTime): Integer;
implementation
uses strutils;
const
cr = chr(13)+chr(10);
tab = chr(9);
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);
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);
function PosStr(const FindString, SourceString: string; StartPos: Integer): Integer;
begin
Result := PosEx(FindString, SourceString, StartPos);
end;
(*
{$asmmode intel}
function PosStr(const FindString, SourceString: string; StartPos: Integer): Integer; assembler;
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;
begin
Result := PosEx(FindString, SourceString, StartPos);
end;
(*
function PosText(const FindString, SourceString: string; StartPos: Integer): Integer; assembler;
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 Contains(const value:variant;const aset:string):boolean;
var
s:string;
p1,p2,L:integer;
begin
result:=false;
s:=value;
L:=length(aset);
p1:=postext(s,aset);
if p1=0 then exit;
// check before
p2:=p1+length(s);
if p1>1 then begin
if aset[p1-1]<>'''' then begin
while (p1>0) and (aset[p1]=' ') do
dec(p1);
if (p1>0) then
if aset[p1]<>',' then exit;
end
end;
// check after
if (p2<=L) then begin
if aset[p2]<>'''' then begin
while (p2<=L) and (aset[p2]=' ') do
inc(p2);
if (p2<=L) then
if aset[p2]<>',' then exit;
end;
end;
result:=true;
end;
procedure SaveString(aFile, aText:string);
begin
with TFileStream.Create(aFile, fmCreate) do try
writeBuffer(aText[1],length(aText));
finally free; end;
end;
function LoadString(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;
function Soundex(source:string) : integer;
Const
{This table gives the SoundEX SCORE for all characters Upper and Lower Case
hence no need to convert. This is faster than doing an UpCase on the whole input string
The 5 NON Chars in middle are just given 0}
SoundExTable : Array[65..122] Of Byte
//A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ / ] ^ _ '
=(0,1,2,3,0,1,2,0,0,2,2,4,5,5,0,1,2,6,2,3,0,1,0,2,0,2,0,0,0,0,0,0,
//a b c d e f g h i j k l m n o p q r s t u v w x y z
0,1,2,3,0,1,2,0,0,2,2,4,5,5,0,1,2,6,2,3,0,1,0,2,0,2);
Var
i, l, s, SO, x : Byte;
Multiple : Word;
Name : PChar;
begin
If source<>'' //do nothing if nothing is passed
then begin
name:=pchar(source);
Result := Ord(UpCase(Name[0])); //initialise to first character
SO := 0; //initialise last char as 0
Multiple := 26; //initialise to 26 char of alphabet
l := Pred(StrLen(Name)); //get into var to save repeating function
For i := 1 to l do //for each char of input str
begin
s := Ord(name[i]); //*
If (s > 64) and (s < 123) //see notes * below
then begin
x := SoundExTable[s]; //get soundex value
If (x > 0) //it is a scoring char
AND (x <> SO) //is different from previous char
then begin
Result := Result + (x * Multiple); //avoid use of POW as it needs maths unit
If (Multiple = 26 * 6 * 6) //we have done enough (NB compiles to a const
then break; //We have done, so leave loop
Multiple := Multiple * 6;
SO := x; //save for next round
end; // of if a scoring char
end; //of if in range of SoundEx table
end; //of for loop
end else result := 0;
end; //of function SoundBts
procedure ListSections(atext:string;list:TStrings);
var
p1,p2:integer;
begin
list.clear;
p1:=1;
repeat
p1:=posstr('[',atext,p1);
if p1>0 then begin
p2:=posstr(']',atext,p1);
if p2=0 then
p1:=0
else begin
list.append(copy(atext,p1+1,p2-(p1+1)));
p1:=p2;
end;
end;
until p1=0;
end;
function GetSection(atext,asection:string):string;
var
p1,p2:integer;
begin
result:='';
p1:=postext('['+asection+']',atext);
if p1=0 then exit;
p1:=p1+length('['+asection+']');
p2:=posstr('[',atext,p1);
if p2=0 then
result:=trim(copy(atext,p1,maxint))
else
result:=trim(copy(atext,p1,p2-p1));
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;
function DateToSQLString(adate:TDateTime):string;
var
ayear,amonth,aday:word;
begin
decodedate(adate,ayear,amonth,aday);
result:=format('%.4d',[ayear])+'-'+format('%.2d',[amonth])+'-'+format('%.2d',[aday]);
end;
function SQLStringToDate(atext:string):TDateTime;
begin
result:=0;
try
result:=encodedate(strtoint(copy(atext,1,4)),strtoint(copy(atext,6,2)),strtoint(copy(atext,9,2)));
except
end;
end;
function Date2Year (const DT: TDateTime): Word;
var
D, M: Word;
begin
DecodeDate (DT, Result, M, D);
end;
function GetFirstDayOfYear (const Year: Word): TDateTime;
begin
Result := EncodeDate (Year, 1, 1);
end;
function StartOfWeek (const DT: TDateTime): TDateTime;
begin
Result := DT - DayOfWeek (DT) + 1;
end;
function DaysApart (const DT1, DT2: TDateTime): LongInt;
begin
Result := Trunc (DT2) - Trunc (DT1);
end;
function Date2WeekNo (const DT: TDateTime): Integer;
var
Year: Word;
FirstSunday, StartYear: TDateTime;
WeekOfs: Byte;
begin
Year := Date2Year (DT);
StartYear := GetFirstDayOfYear (Year);
if DayOfWeek (StartYear) = 0 then
begin
FirstSunday := StartYear;
WeekOfs := 1;
end
else
begin
FirstSunday := StartOfWeek (StartYear) + 7;
WeekOfs := 2;
if DT < FirstSunday then
begin
Result := 1;
Exit;
end;
end;
Result := DaysApart (FirstSunday, StartofWeek (DT)) div 7 + WeekOfs;
end;
end.