mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 01:24:18 +02:00
668 lines
16 KiB
ObjectPascal
668 lines
16 KiB
ObjectPascal
unit JcfStringUtils;
|
|
|
|
{(*}
|
|
(*------------------------------------------------------------------------------
|
|
Delphi Code formatter source code
|
|
|
|
The Original Code is JcfStringUtils, released October 2008.
|
|
The Initial Developer of the Original Code is Paul Ishenin
|
|
Portions created by Paul Ishenin are Copyright (C) 1999-2008 Paul Ishenin
|
|
All Rights Reserved.
|
|
Contributor(s): Anthony Steele.
|
|
|
|
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/
|
|
|
|
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.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 2 or later (the "GPL")
|
|
See http://www.gnu.org/licenses/gpl.html
|
|
------------------------------------------------------------------------------*)
|
|
{*)}
|
|
|
|
{$I JcfGlobal.inc}
|
|
|
|
{
|
|
This unit contains string utility code
|
|
For use when the JCL string functions are not avaialable
|
|
}
|
|
|
|
interface
|
|
uses
|
|
SysUtils, Classes;
|
|
|
|
const
|
|
NativeNull = Char(#0);
|
|
NativeSoh = Char(#1);
|
|
NativeStx = Char(#2);
|
|
NativeEtx = Char(#3);
|
|
NativeEot = Char(#4);
|
|
NativeEnq = Char(#5);
|
|
NativeAck = Char(#6);
|
|
NativeBell = Char(#7);
|
|
NativeBackspace = Char(#8);
|
|
NativeTab = Char(#9);
|
|
NativeLineFeed = AnsiChar(#10);
|
|
NativeVerticalTab = Char(#11);
|
|
NativeFormFeed = Char(#12);
|
|
NativeCarriageReturn = AnsiChar(#13);
|
|
NativeCrLf = AnsiString(#13#10);
|
|
NativeSo = Char(#14);
|
|
NativeSi = Char(#15);
|
|
NativeDle = Char(#16);
|
|
NativeDc1 = Char(#17);
|
|
NativeDc2 = Char(#18);
|
|
NativeDc3 = Char(#19);
|
|
NativeDc4 = Char(#20);
|
|
NativeNak = Char(#21);
|
|
NativeSyn = Char(#22);
|
|
NativeEtb = Char(#23);
|
|
NativeCan = Char(#24);
|
|
NativeEm = Char(#25);
|
|
NativeEndOfFile = Char(#26);
|
|
NativeEscape = Char(#27);
|
|
NativeFs = Char(#28);
|
|
NativeGs = Char(#29);
|
|
NativeRs = Char(#30);
|
|
NativeUs = Char(#31);
|
|
NativeSpace = Char(' ');
|
|
NativeComma = Char(',');
|
|
NativeBackslash = Char('\');
|
|
NativeForwardSlash = Char('/');
|
|
|
|
{$IFDEF MSWINDOWS}
|
|
NativeLineBreak = NativeCrLf;
|
|
PathSeparator = '\';
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF UNIX}
|
|
NativeLineBreak = NativeLineFeed;
|
|
PathSeparator = '/';
|
|
{$ENDIF UNIX}
|
|
DirDelimiter = PathSeparator;
|
|
NativeHexDigits = ['0'..'9', 'A'..'F', 'a'..'f'];
|
|
NativeWhiteSpace = [NativeTab, NativeLineFeed, NativeVerticalTab,
|
|
NativeFormFeed, NativeCarriageReturn, NativeSpace];
|
|
|
|
NativeDoubleQuote = Char('"');
|
|
NativeSingleQuote = Char('''');
|
|
|
|
|
|
function CharIsControl(const C: Char): Boolean;
|
|
function CharIsAlpha(const C: Char): Boolean;
|
|
function CharIsAlphaNum(const C: Char): Boolean;
|
|
function CharIsDigit(const C: Char): Boolean;
|
|
function CharIsReturn(const C: Char): Boolean;
|
|
function CharIsWhiteSpace(const C: Char): Boolean;
|
|
|
|
function CharUpper(const C: Char): Char;
|
|
|
|
function StrIsAlpha(const S: string): Boolean;
|
|
function StrIsAlphaNum(const S: string): Boolean;
|
|
|
|
function StrTrimQuotes(const S: string): string;
|
|
|
|
function StrAfter(const SubStr, S: string): string;
|
|
function StrBefore(const SubStr, S: string): string;
|
|
function StrChopRight(const S: string; N: Integer): string;
|
|
function StrLastPos(const SubStr, S: string): Integer;
|
|
function StrLeft(const S: string; Count: Integer): string;
|
|
function StrRestOf(const S: string; N: Integer ): string;
|
|
function StrRight(const S: string; Count: Integer): string;
|
|
|
|
function StrDoubleQuote(const S: string): string;
|
|
function StrSmartCase(const S: string; Delimiters: TSysCharSet): string;
|
|
|
|
function StrCharCount(const S: string; C: Char): Integer;
|
|
function StrStrCount(const S, SubS: string): Integer;
|
|
function StrRepeat(const S: string; Count: Integer): string;
|
|
procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags = []);
|
|
function StrSearch(const Substr, S: string; const Index: Integer = 1): Integer;
|
|
|
|
function BooleanToStr(B: Boolean): string;
|
|
function StrToBoolean(const S: string): Boolean;
|
|
|
|
function StrFind(const Substr, S: string; const Index: Integer = 1): Integer;
|
|
function StrIsOneOf(const S: string; const List: array of string): Boolean;
|
|
|
|
procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean = True);
|
|
|
|
function FileToString(const FileName: string): AnsiString;
|
|
procedure StringToFile(const FileName: string; const Contents: AnsiString);
|
|
function StrFillChar(const C: Char; Count: Integer): string;
|
|
function IntToStrZeroPad(Value, Count: Integer): String;
|
|
function WideStringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags): WideString;
|
|
|
|
function PathExtractFileNameNoExt(const Path: string): string;
|
|
function GetWindowsTempFolder: string;
|
|
function FileGetSize(const FileName: string): Int64;
|
|
procedure ShellExecEx(const FileName: string; const Parameters: string = '');
|
|
function GetTickCount: Cardinal;
|
|
function IsMultiByte(const pcChar: WideChar): Boolean;
|
|
|
|
function IsWinVista: Boolean;
|
|
function IsWinXP: Boolean;
|
|
function IsWin2k: Boolean;
|
|
function IsWin2003: Boolean;
|
|
|
|
type
|
|
EJcfConversionError = class(Exception)
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef MSWINDOWS}
|
|
Windows, ShellApi
|
|
{$endif}
|
|
{$ifdef Unix}
|
|
Unix
|
|
{$endif}
|
|
{$ifdef fpc}
|
|
, LCLIntf, FileUtil
|
|
{$endif};
|
|
|
|
{$IFNDEF DELPHI12}
|
|
|
|
// define CharInSet for Delphi 2007 or earlier
|
|
function CharInSet(const C: Char; const testSet: TSysCharSet): boolean;
|
|
begin
|
|
Result := C in testSet;
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
function CharIsAlpha(const C: Char): Boolean;
|
|
begin
|
|
Result := CharInSet(C, ['a'..'z','A'..'Z']);
|
|
end;
|
|
|
|
function CharIsAlphaNum(const C: Char): Boolean;
|
|
begin
|
|
Result := CharIsAlpha(C) or CharIsDigit(C);
|
|
end;
|
|
|
|
function CharIsControl(const C: Char): Boolean;
|
|
begin
|
|
Result := C <= #31;
|
|
end;
|
|
|
|
function CharIsDigit(const C: Char): Boolean;
|
|
begin
|
|
Result := CharInSet(C, ['0'..'9']);
|
|
end;
|
|
|
|
function CharIsReturn(const C: Char): Boolean;
|
|
begin
|
|
Result := CharInSet(C, [NativeLineFeed, NativeCarriageReturn]);
|
|
end;
|
|
|
|
function CharIsWhiteSpace(const C: Char): Boolean;
|
|
begin
|
|
Result := CharInSet(C, NativeWhiteSpace) ;
|
|
end;
|
|
|
|
function CharUpper(const C: Char): Char;
|
|
begin
|
|
// Paul: original code used char case table
|
|
Result := UpCase(C);
|
|
end;
|
|
|
|
function StrIsAlpha(const S: string): Boolean;
|
|
var
|
|
I, L: integer;
|
|
begin
|
|
L := Length(S);
|
|
Result := L > 0;
|
|
for I := 1 to L do
|
|
if not CharIsAlpha(S[I]) then
|
|
begin
|
|
Result := False;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function StrIsAlphaNum(const S: string): Boolean;
|
|
var
|
|
I, L: integer;
|
|
begin
|
|
L := Length(S);
|
|
Result := L > 0;
|
|
for I := 1 to L do
|
|
if not CharIsAlphaNum(S[I]) then
|
|
begin
|
|
Result := False;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function StrTrimQuotes(const S: string): string;
|
|
var
|
|
C1, C2: Char;
|
|
L: Integer;
|
|
begin
|
|
Result := S;
|
|
L := Length(Result);
|
|
if L >= 2 then
|
|
begin
|
|
C1 := Result[1];
|
|
C2 := Result[L];
|
|
if (C1 = C2) and (CharInSet(C1, [NativeSingleQuote, NativeDoubleQuote])) then
|
|
begin
|
|
Delete(Result, L, 1);
|
|
Delete(Result, 1, 1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function StrAfter(const SubStr, S: string): string;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
P := StrSearch(SubStr, S, 1);
|
|
if P > 0 then
|
|
Result := Copy(S, P + Length(SubStr), Length(S))
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function StrBefore(const SubStr, S: string): string;
|
|
var
|
|
P: Integer;
|
|
begin
|
|
P := StrSearch(SubStr, S, 1);
|
|
if P > 0 then
|
|
Result := Copy(S, 1, P - 1)
|
|
else
|
|
Result := S;
|
|
end;
|
|
|
|
function StrChopRight(const S: string; N: Integer): string;
|
|
begin
|
|
Result := Copy(S, 1, Length(S) - N);
|
|
end;
|
|
|
|
function StrLastPos(const SubStr, S: string): Integer;
|
|
var
|
|
NewPos: Integer;
|
|
begin
|
|
Result := 0;
|
|
while Result < Length(S) do
|
|
begin
|
|
NewPos := StrSearch(SubStr, S, Result + 1);
|
|
if NewPos > 0 then
|
|
Result := NewPos
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function StrLeft(const S: string; Count: Integer): string;
|
|
begin
|
|
Result := Copy(S, 1, Count);
|
|
end;
|
|
|
|
function StrRestOf(const S: string; N: Integer ): string;
|
|
begin
|
|
Result := Copy(S, N, (Length(S) - N + 1));
|
|
end;
|
|
|
|
function StrRight(const S: string; Count: Integer): string;
|
|
begin
|
|
Result := Copy(S, Length(S) - Count + 1, Count);
|
|
end;
|
|
|
|
function StrDoubleQuote(const S: string): string;
|
|
begin
|
|
Result := NativeDoubleQuote + S + NativeDoubleQuote;
|
|
end;
|
|
|
|
function StrSmartCase(const S: string; Delimiters: TSysCharSet): string;
|
|
var
|
|
i: integer;
|
|
begin
|
|
// if no delimiters passed then use default set
|
|
if Delimiters = [] then
|
|
Delimiters := NativeWhiteSpace;
|
|
Result := S;
|
|
for i := 1 to Length(Result) do
|
|
if (i = 1) or (CharInSet(Result[i - 1], Delimiters)) then
|
|
Result[i] := UpCase(Result[i]);
|
|
end;
|
|
|
|
function StrCharCount(const S: string; C: Char): Integer;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 1 to Length(S) do
|
|
if S[i] = C then
|
|
inc(Result);
|
|
end;
|
|
|
|
function StrStrCount(const S, SubS: string): Integer;
|
|
var
|
|
P: integer;
|
|
begin
|
|
Result := 0;
|
|
P := 1;
|
|
while P < Length(S) do
|
|
begin
|
|
P := StrSearch(Subs, S, P);
|
|
if P > 0 then
|
|
begin
|
|
inc(Result);
|
|
inc(P);
|
|
end
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function StrRepeat(const S: string; Count: Integer): string;
|
|
begin
|
|
Result := '';
|
|
while Count > 0 do
|
|
begin
|
|
Result := Result + S;
|
|
Dec(Count);
|
|
end;
|
|
end;
|
|
|
|
procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags = []);
|
|
begin
|
|
S := StringReplace(S, Search, Replace, Flags);
|
|
end;
|
|
|
|
function StrSearch(const Substr, S: string; const Index: Integer = 1): Integer;
|
|
begin
|
|
// Paul: I expect original code was more efficient :)
|
|
Result := Pos(SubStr, Copy(S, Index, Length(S)));
|
|
|
|
if Result > 0 then
|
|
Result := Result + Index - 1;
|
|
end;
|
|
|
|
function BooleanToStr(B: Boolean): string;
|
|
const
|
|
BoolToStrMap: array[Boolean] of String =
|
|
(
|
|
{ false } 'False',
|
|
{ true } 'True'
|
|
);
|
|
begin
|
|
Result := BoolToStrMap[B];
|
|
end;
|
|
|
|
function StrToBoolean(const S: string): Boolean;
|
|
var
|
|
LowerS: String;
|
|
begin
|
|
LowerS := LowerCase(S);
|
|
if (LowerS = 'false') or (LowerS = 'no') or (LowerS = '0') then
|
|
Result := False
|
|
else
|
|
if (LowerS = 'true') or (LowerS = 'yes') or (LowerS = '1') or (LowerS = '-1') then
|
|
Result := True
|
|
else
|
|
raise EJcfConversionError.Create('Cannot convert string [' + S + '] to boolean');
|
|
end;
|
|
|
|
|
|
function StrFind(const Substr, S: string; const Index: Integer = 1): Integer;
|
|
begin
|
|
// Paul: original code used comparision by char case table
|
|
Result := StrSearch(LowerCase(SubStr), LowerCase(S), Index);
|
|
end;
|
|
|
|
function StrIsOneOf(const S: string; const List: array of string): Boolean;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Low(List) to High(List) do
|
|
if CompareStr(List[i], S) = 0 then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TrimStrings(const List: TStrings; DeleteIfEmpty: Boolean = True);
|
|
var
|
|
i: integer;
|
|
begin
|
|
if List <> nil then
|
|
for i := List.Count - 1 downto 0 do
|
|
begin
|
|
List[i] := Trim(List[i]);
|
|
if DeleteIfEmpty and (List[i] = '') then
|
|
List.Delete(i);
|
|
end;
|
|
end;
|
|
|
|
function FileToString(const FileName: string): AnsiString;
|
|
var
|
|
S: TStream;
|
|
begin
|
|
S := nil;
|
|
try
|
|
S := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
|
|
SetLength(Result, S.Size);
|
|
S.Read(PAnsiChar(Result)^, S.Size);
|
|
finally
|
|
S.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure StringToFile(const FileName: string; const Contents: AnsiString);
|
|
var
|
|
S: TStream;
|
|
begin
|
|
S := nil;
|
|
try
|
|
S := TFileStream.Create(FileName, fmCreate);
|
|
S.Write(PAnsiChar(Contents)^, Length(Contents));
|
|
finally
|
|
S.Free;
|
|
end;
|
|
end;
|
|
|
|
function StrFillChar(const C: Char; Count: Integer): string;
|
|
begin
|
|
SetLength(Result, Count);
|
|
if Count > 0 then
|
|
FillChar(Result[1], Count, C);
|
|
end;
|
|
|
|
function IntToStrZeroPad(Value, Count: Integer): String;
|
|
begin
|
|
Result := IntToStr(Value);
|
|
while Length(Result) < Count do
|
|
Result := '0' + Result;
|
|
end;
|
|
|
|
// Based on FreePascal version of StringReplace
|
|
function WideStringReplace(const S, OldPattern, NewPattern: WideString; Flags: TReplaceFlags): WideString;
|
|
var
|
|
Srch, OldP, RemS: WideString; // Srch and Oldp can contain uppercase versions of S,OldPattern
|
|
P: Integer;
|
|
begin
|
|
Srch := S;
|
|
OldP := OldPattern;
|
|
if rfIgnoreCase in Flags then
|
|
begin
|
|
Srch := WideUpperCase(Srch);
|
|
OldP := WideUpperCase(OldP);
|
|
end;
|
|
RemS := S;
|
|
Result := '';
|
|
while (Length(Srch) <> 0) do
|
|
begin
|
|
P := Pos(OldP, Srch);
|
|
if P = 0 then
|
|
begin
|
|
Result := Result + RemS;
|
|
Srch := '';
|
|
end
|
|
else
|
|
begin
|
|
Result := Result + Copy(RemS, 1, P - 1) + NewPattern;
|
|
P := P + Length(OldP);
|
|
RemS := Copy(RemS, P, Length(RemS) - P + 1);
|
|
if not (rfReplaceAll in Flags) then
|
|
begin
|
|
Result := Result + RemS;
|
|
Srch := '';
|
|
end
|
|
else
|
|
Srch := Copy(Srch, P, Length(Srch) - P + 1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function PathRemoveExtension(const Path: string): string;
|
|
var
|
|
p: Integer;
|
|
begin
|
|
// from Lazarus FileUtil
|
|
Result := Path;
|
|
p := Length(Result);
|
|
while (p>0) do
|
|
begin
|
|
case Result[p] of
|
|
PathDelim: Exit;
|
|
'.': Result := copy(Result, 1, p-1);
|
|
end;
|
|
Dec(p);
|
|
end;
|
|
end;
|
|
|
|
function PathExtractFileNameNoExt(const Path: string): string;
|
|
begin
|
|
Result := PathRemoveExtension(ExtractFileName(Path));
|
|
end;
|
|
|
|
function PathRemoveSeparator(const Path: string): string;
|
|
begin
|
|
Result := Path;
|
|
if (Result <> '') and (Result[Length(Result)] = PathDelim) then
|
|
Delete(Result, Length(Result), 1);
|
|
end;
|
|
|
|
function GetWindowsTempFolder: string;
|
|
{$ifndef fpc}
|
|
var
|
|
buf: string;
|
|
{$endif}
|
|
begin
|
|
{$ifdef fpc}
|
|
Result := GetTempDir;
|
|
{$else}
|
|
SetLength(buf, MAX_PATH);
|
|
SetLength(buf, GetTempPath(Length(buf) + SizeOf(char), PChar(buf)));
|
|
Result:=buf;
|
|
Result := IncludeTrailingPathDelimiter(Result);
|
|
{$endif}
|
|
end;
|
|
|
|
// We know that this unit contains platform-specific code
|
|
// it's guarded by ifdefs
|
|
{$WARN SYMBOL_PLATFORM OFF}
|
|
|
|
function FileGetSize(const FileName: string): Int64;
|
|
{$ifndef fpc}
|
|
var
|
|
FileInfo: TSearchRec;
|
|
{$endif}
|
|
begin
|
|
{$ifdef fpc}
|
|
Result := FileUtil.FileSize(FileName);
|
|
{$else}
|
|
// from LCL FileUtil code
|
|
FileInfo.Name := Filename;
|
|
FileInfo.FindHandle := Windows.FindFirstFile(Windows.LPTSTR(FileInfo.Name), FileInfo.FindData);
|
|
if FileInfo.FindHandle = Windows.Invalid_Handle_value then
|
|
begin
|
|
Result:=-1;
|
|
Exit;
|
|
end;
|
|
Result := (int64(FileInfo.FindData.nFileSizeHigh) shl 32) + FileInfo.FindData.nFileSizeLow;
|
|
Windows.FindClose(FileInfo.FindHandle);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure ShellExecEx(const FileName: string; const Parameters: string = '');
|
|
begin
|
|
{$ifdef MSWINDOWS}
|
|
ShellApi.ShellExecute(0, 'open', PChar(FileName), PChar(Parameters), nil, SW_SHOW);
|
|
{$endif}
|
|
{$ifdef unix}
|
|
Shell(format('%s %s',[FileName, Parameters]));
|
|
{$endif}
|
|
end;
|
|
|
|
function GetTickCount: DWord;
|
|
begin
|
|
{$ifdef MSWINDOWS}
|
|
Result := Windows.GetTickCount;
|
|
{$else}
|
|
Result := LCLIntf.GetTickCount;
|
|
{$endif}
|
|
end;
|
|
|
|
function IsMultiByte(const pcChar: WideChar): Boolean;
|
|
begin
|
|
{$ifdef MSWINDOWS}
|
|
Result := IsDBCSLeadByte(Byte(pcChar));
|
|
{$else}
|
|
Result := False;
|
|
// TODO: ?
|
|
{$endif}
|
|
end;
|
|
|
|
function IsWinVista: Boolean;
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := Win32MajorVersion = 6;
|
|
// can be also window server 2008
|
|
{$ELSE}
|
|
Result := False;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function IsWinXP: Boolean;
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := (Win32MajorVersion = 5) and (Win32MinorVersion = 1);
|
|
{$ELSE}
|
|
Result := False;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function IsWin2k: Boolean;
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := (Win32MajorVersion = 5) and (Win32MinorVersion = 0);
|
|
{$ELSE}
|
|
Result := False;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function IsWin2003: Boolean;
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := (Win32MajorVersion = 5) and (Win32MinorVersion = 2);
|
|
// can be also window xp 64 bit
|
|
{$ELSE}
|
|
Result := False;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
end.
|