1062 lines
29 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
Useful functions dealing with strings.
Copyright (C) 2006-2014 Alexander Koblov (alexx2000@mail.ru)
Copyright (C) 2012 Przemyslaw Nagay (cobines@gmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit DCStrUtils;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DCBasicTypes;
type
TPathType = (ptNone, ptRelative, ptAbsolute);
{en
Checks if StringToCheck contains any of the single characters in
PossibleCharacters. Only ASCII can be searched.
}
function ContainsOneOf(StringToCheck: UTF8String; PossibleCharacters: String): Boolean;
{en
Convert known directory separators to the current directory separator.
}
function NormalizePathDelimiters(const Path: String): String;
{en
Get last directory name in path
@returns(Last directory name in path)
}
function GetLastDir(Path : String) : String;
{en
Retrieves the root directory for a path.
@param(sPath Absolute path to a directory or a file.)
@returns(Root directory or an empty string if the path is not absolute.)
}
function GetRootDir(sPath : String) : String;
{en
Retrieves parent directory for a path (removes the last subdirectory in the path).
@param(sPath Absolute or relative path to a directory or a file.)
@returns(Parent directory or an empty string
if the path does not have a parent directory.)
}
function GetParentDir(sPath : String) : String;
{en
Gets the deepest (longest) path that exist.
}
function GetDeepestExistingPath(const sPath : UTF8String) : UTF8String;
function GetSplitFileName(var sFileName, sPath : String) : String;
function MakeFileName(const sPath, sFileNameDef : String) : String;
{en
Split path into list of directories
@param(DirName Path)
@param(Dirs List of directories names)
@returns(The function returns the number of directories found, or -1
if none were found.)
}
function GetDirs (DirName : String; var Dirs : TStringList) : Longint;
{en
Get absolute file name from relative file name
@param(sPath Current path)
@param(sRelativeFileName Relative file name)
@returns(Absolute file name)
}
function GetAbsoluteFileName(const sPath, sRelativeFileName : String) : String;
{en
Checks if a path to a directory or file is absolute or relative.
@returns(ptNone if a path is just a directory or file name (MyDir)
ptRelative if a path is relative (MyDir/MySubDir)
ptAbsolute if a path is absolute) (/root/MyDir)
}
function GetPathType(const sPath : String): TPathType;
{en
Get file name without path and extension
@param(FileName File name)
@returns(File name without path and extension)
}
function ExtractOnlyFileName(const FileName: string): string;
{en
Get file extension without the '.' at the front.
}
function ExtractOnlyFileExt(const FileName: string): string;
{en
Remove file extension with the '.' from file name.
}
function RemoveFileExt(const FileName: UTF8String): UTF8String;
function ContainsWildcards(const Path: UTF8String): Boolean;
{en
Expands an absolute file path by removing all relative references.
Processes '/../' and '/./'.
Example: /home/user/files/../somedirectory/./file.txt
= /home/user/somedirectory/file.txt
@param(Path path to expand.)
}
function ExpandAbsolutePath(const Path: String): String;
function HasPathInvalidCharacters(Path: UTF8String): Boolean;
{en
Checks if a file or directory belongs in the specified path.
Only strings are compared, no file-system checks are done.
@param(sBasePath
Absolute path where the path to check should be in.)
@param(sPathToCheck
Absolute path to file or directory to check.)
@param(AllowSubDirs
If @true, allows the sPathToCheck to point to a file or directory in some subdirectory of sBasePath.
If @false, only allows the sPathToCheck to point directly to a file or directory in sBasePath.)
@param(AllowSame
If @true, returns @true if sBasePath = sPathToCheck.
If @false, returns @false if sBasePath = sPathToCheck.)
@return(@true if sPathToCheck points to a directory or file in sBasePath.
@false otherwise.)
Examples:
IsInPath('/home', '/home/somedir/somefile', True, False) = True
IsInPath('/home', '/home/somedir/somefile', False, False) = False
IsInPath('/home', '/home/somedir/', False, False) = True
IsInPath('/home', '/home', False, False) = False
IsInPath('/home', '/home', False, True) = True
}
function IsInPath(sBasePath : String; sPathToCheck : String;
AllowSubDirs : Boolean; AllowSame : Boolean) : Boolean;
{en
Changes a path to be relative to some parent directory.
@param(sPrefix
Absolute path that is a parent of sPath.)
@param(sPath
Path to change. Must be a subpath of sPrefix, otherwise no change is made.)
Examples:
ExtractDirLevel('/home', '/home/somedir/somefile') = '/somedir/somefile'
}
function ExtractDirLevel(const sPrefix, sPath: String): String;
{en
Adds a path delimiter at the beginning of the string, if it not exists.
}
function IncludeFrontPathDelimiter(s: String): String;
{en
Removes a path delimiter at the beginning of the string, if it exists.
}
function ExcludeFrontPathDelimiter(s: String): String;
{en
Removes a path delimiter at the ending of the string, if it exists.
Doesn't remove path delimiter if it is the only character in the path (root dir),
so it is safer to use than ExcludeTrailingPathDelimiter, especially on Unix.
}
function ExcludeBackPathDelimiter(const Path: UTF8String): UTF8String;
{en
Return position of character in string begun from start position
@param(C character)
@param(S String)
@param(StartPos Start position)
@returns(Position of character in string)
}
function CharPos(C: Char; const S: string; StartPos: Integer = 1): Integer;
{en
Split file name on name and extension
@param(sFileName File name)
@param(n Name)
@param(e Extension)
}
procedure DivFileName(const sFileName:String; out n,e:String);
{en
Split file mask on name mask and extension mask
@param(DestMask File mask)
@param(DestNameMask Name mask)
@param(DestExtMask Extension mask)
}
procedure SplitFileMask(const DestMask: String; out DestNameMask: String; out DestExtMask: String);
{en
Apply name and extension mask to the file name
@param(aFileName File name)
@param(NameMask Name mask)
@param(ExtMask Extension mask)
}
function ApplyRenameMask(aFileName: String; NameMask: String; ExtMask: String): String;
{en
Get count of character in string
@param(Char Character)
@param(S String)
@returns(Count of character)
}
function NumCountChars(const Char: Char; const S: String): Integer;
{en
Remove last line ending in text
@param(sText Text)
@param(TextLineBreakStyle Text line break style)
}
function TrimRightLineEnding(const sText: UTF8String; TextLineBreakStyle: TTextLineBreakStyle): UTF8String;
function mbCompareText(const s1, s2: UTF8String): PtrInt;
function StrNewW(const mbString: UTF8String): PWideChar;
procedure StrDisposeW(var pStr : PWideChar);
function StrLCopyW(Dest, Source: PWideChar; MaxLen: SizeInt): PWideChar;
function StrPCopyW(Dest: PWideChar; const Source: WideString): PWideChar;
function StrPLCopyW(Dest: PWideChar; const Source: WideString; MaxLen: Cardinal): PWideChar;
{en
Checks if a string begins with another string.
@returns(@true if StringToCheck begins with StringToMatch.
StringToCheck may be longer than StringToMatch.)
}
function StrBegins(const StringToCheck, StringToMatch: String): Boolean;
{en
Checks if a string ends with another string.
@returns(@true if StringToCheck ends with StringToMatch.
StringToCheck may be longer than StringToMatch.)
}
function StrEnds(const StringToCheck, StringToMatch: String): Boolean;
{en
Adds a string to another string. If the source string is not empty adds
a separator before adding the string.
}
procedure AddStrWithSep(var SourceString: String; const StringToAdd: String; const Separator: Char = ' ');
procedure AddStrWithSep(var SourceString: String; const StringToAdd: String; const Separator: String);
procedure ParseLineToList(sLine: String; ssItems: TStrings);
{en
Convert a number specified as an octal number to it's decimal value.
@param(Value Octal number as string)
@returns(Decimal number)
}
function OctToDec(Value: String): LongInt;
{en
Convert a number specified as an decimal number to it's octal value.
@param(Value Decimal number)
@returns(Octal number as string)
}
function DecToOct(Value: LongInt): String;
procedure AddString(var anArray: TDynamicStringArray; const sToAdd: String);
{en
Splits a string into different parts delimited by the specified delimiter character.
}
function SplitString(const S: String; Delimiter: AnsiChar): TDynamicStringArray;
{en
Checks if the second array is the beginning of first.
If BothWays is @true then also checks the other way around,
if the first array is the beginning of second.
For Array1=[1,2] Array2=[1,2] returns @true.
For Array1=[1,2,...] Array2=[1,2] returns @true.
For Array1=[1,3,...] Array2=[1,2] returns @false.
If BothWays = True then also
For Array1=[1] Array2=[1,2] returns @true.
For Array1=[1] Array2=[2] returns @false.
}
function ArrBegins(const Array1, Array2: array of String; BothWays: Boolean): Boolean;
function ArrayToString(const anArray: TDynamicStringArray; const Separator: Char = ' '): String;
{en
Compares length and contents of the arrays.
If lengths differ or individual elements differ returns @false, otherwise @true.
}
function Compare(const Array1, Array2: array of String): Boolean;
{en
Copies open array to dynamic array.
}
function CopyArray(const anArray: array of String): TDynamicStringArray;
function ContainsOneOf(const ArrayToSearch, StringsToSearch: array of String): Boolean;
function Contains(const ArrayToSearch: array of String; const StringToSearch: String): Boolean;
procedure DeleteString(var anArray: TDynamicStringArray; const Index: Integer);
procedure DeleteString(var anArray: TDynamicStringArray; const sToDelete: String);
function GetArrayFromStrings(Strings: TStrings): TDynamicStringArray;
procedure SetStringsFromArray(Strings: TStrings; const anArray: TDynamicStringArray);
{en
Replaces old value of Key or adds a new Key=NewValue string to the array.
}
procedure SetValue(var anArray: TDynamicStringArray; Key, NewValue: String);
procedure SetValue(var anArray: TDynamicStringArray; Key: String; NewValue: Boolean);
function ShortcutsToText(const Shortcuts: TDynamicStringArray): String;
implementation
uses
DCOSUtils;
function NormalizePathDelimiters(const Path: String): String;
{$IFDEF UNIX}
begin
Result:= Path;
end;
{$ELSE}
const
AllowPathDelimiters : set of char = ['\','/'];
var
I : LongInt;
begin
Result:= Path;
// If path is not URI
if Pos('://', Result) = 0 then
begin
for I:= 1 to Length(Path) do
if Path[I] in AllowPathDelimiters then
Result[I]:= DirectorySeparator;
end;
end;
{$ENDIF}
function GetLastDir(Path : String) : String;
begin
Result:= ExtractFileName(ExcludeTrailingPathDelimiter(Path));
if Result = '' then
Result:= ExtractFileDrive(Path);
if Result = '' then
Result:= PathDelim;
end;
function GetRootDir(sPath : String) : String;
begin
{$IF DEFINED(MSWINDOWS)}
Result := ExtractFileDrive(sPath);
if Result <> '' then
Result := Result + PathDelim;
{$ELSEIF DEFINED(UNIX)}
Result := PathDelim; // Hardcoded
{$ELSE}
Result := '';
{$ENDIF}
end;
function GetParentDir(sPath : String) : String;
var
i : Integer;
begin
Result := '';
sPath := ExcludeTrailingPathDelimiter(sPath);
// Start from one character before last.
for i := length(sPath) - 1 downto 1 do
if sPath[i] = DirectorySeparator then
begin
Result := Copy(sPath, 1, i);
Break;
end;
end;
function GetDeepestExistingPath(const sPath : UTF8String) : UTF8String;
begin
Result := sPath;
while Result <> EmptyStr do
begin
if not mbDirectoryExists(Result) then
Result := GetParentDir(Result)
else
Break;
end;
end;
function GetSplitFileName(var sFileName, sPath : String) : String;
begin
if Pos(PathDelim, sFileName) <> 0 then
begin
Result := sFileName;
sPath := ExtractFilePath(sFileName);
sFileName := ExtractFileName(sFileName);
end
else
Result := sPath + sFileName;
end;
function MakeFileName(const sPath, sFileNameDef : String) : String;
begin
Result:= ExtractFileName(ExcludeTrailingBackslash(sPath));
if Result = EmptyStr then
Result:= sFileNameDef;
end;
function GetDirs (DirName : String; var Dirs : TStringList) : Longint;
var
I : Longint;
len : Integer;
sDir : String;
begin
I:= 1;
Result:= -1;
len := Length(DirName);
while I <= len do
begin
if DirName[I]=PathDelim then
begin
Inc(Result);
sDir := Copy(DirName, 1, len - (len - I + 1));
if dirs.IndexOf(sDir) < 0 then
dirs.Add(sDir);
end;
Inc(I);
end;
if Result > -1 then inc(Result);
end;
function GetAbsoluteFileName(const sPath, sRelativeFileName : String) : String;
begin
case GetPathType(sRelativeFileName) of
ptNone:
Result := sPath + sRelativeFileName;
ptRelative:
Result := ExpandAbsolutePath(sPath + sRelativeFileName);
ptAbsolute:
Result := sRelativeFileName;
end;
end;
function GetPathType(const sPath : String): TPathType;
begin
if sPath <> EmptyStr then
begin
{$IFDEF MSWINDOWS}
{ Absolute path in Windows }
if { X:\... [Disk] ":" is reserved otherwise }
( Pos( DriveDelim, sPath ) > 0 ) or
{ \\... [UNC]
\... [Root of current drive] }
( sPath[1] = PathDelim ) then
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
{ UNIX absolute paths start with a slash }
if (sPath[1] = PathDelim) then
{$ENDIF UNIX}
Result := ptAbsolute
else if ( Pos( PathDelim, sPath ) > 0 ) then
Result := ptRelative
else
Result := ptNone;
end
else
Result := ptNone;
end;
function ExtractOnlyFileName(const FileName: string): string;
var
I, Index : LongInt;
EndSep : Set of Char;
begin
// Find a dot index
I := Length(FileName);
EndSep:= AllowDirectorySeparators + AllowDriveSeparators + [ExtensionSeparator];
while (I > 0) and not (FileName[I] in EndSep) do Dec(I);
if (I > 0) and (FileName[I] = ExtensionSeparator) then
Index := I
else
Index := MaxInt;
// Find file name index
EndSep := EndSep - [ExtensionSeparator];
while (I > 0) and not (FileName[I] in EndSep) do Dec(I);
Result := Copy(FileName, I + 1, Index - I - 1);
end;
function ExtractOnlyFileExt(const FileName: string): string;
var
I : LongInt;
EndSep : Set of Char;
begin
I := Length(FileName);
EndSep:= AllowDirectorySeparators + AllowDriveSeparators + [ExtensionSeparator];
while (I > 0) and not (FileName[I] in EndSep) do Dec(I);
if (I > 0) and (FileName[I] = ExtensionSeparator) then
Result := Copy(FileName, I + 1, MaxInt)
else
Result := '';
end;
function RemoveFileExt(const FileName: UTF8String): UTF8String;
var
I : LongInt;
EndSep : Set of Char;
begin
I := Length(FileName);
EndSep:= AllowDirectorySeparators + AllowDriveSeparators + [ExtensionSeparator];
while (I > 0) and not (FileName[I] in EndSep) do
Dec(I);
if (I > 0) and (FileName[I] = ExtensionSeparator) then
Result := Copy(FileName, 1, I - 1)
else
Result := FileName;
end;
function ContainsWildcards(const Path: UTF8String): Boolean;
begin
Result := ContainsOneOf(Path, '*?');
end;
function ExpandAbsolutePath(const Path: String): String;
var
I, J: Integer;
begin
Result := Path;
{First remove all references to '\.\'}
I := Pos (DirectorySeparator + '.' + DirectorySeparator, Result);
while I <> 0 do
begin
Delete (Result, I, 2);
I := Pos (DirectorySeparator + '.' + DirectorySeparator, Result);
end;
if StrEnds(Result, DirectorySeparator + '.') then
Delete (Result, Length(Result) - 1, 2);
{Then remove all references to '\..\'}
I := Pos (DirectorySeparator + '..', Result);
while (I <> 0) do
begin
J := Pred (I);
while (J > 0) and (Result [J] <> DirectorySeparator) do
Dec (J);
if (J = 0) then
Delete (Result, I, 3)
else
Delete (Result, J, I - J + 3);
I := Pos (DirectorySeparator + '..', Result);
end;
end;
function HasPathInvalidCharacters(Path: UTF8String): Boolean;
begin
Result := ContainsOneOf(Path, '*?');
end;
function IsInPath(sBasePath : String; sPathToCheck : String;
AllowSubDirs : Boolean; AllowSame : Boolean) : Boolean;
var
BasePathLength, PathToCheckLength: Integer;
DelimiterPos: Integer;
begin
if sBasePath = '' then Exit(False);
sBasePath := IncludeTrailingPathDelimiter(sBasePath);
BasePathLength := Length(sBasePath);
PathToCheckLength := Length(sPathToCheck);
if PathToCheckLength > BasePathLength then
begin
if CompareStr(Copy(sPathToCheck, 1, BasePathLength), sBasePath) = 0 then
begin
if AllowSubDirs then
Result := True
else
begin
// Additionally check if the remaining path is a relative path.
// Look for a path delimiter in the middle of the filepath.
sPathToCheck := Copy(sPathToCheck, 1 + BasePathLength,
PathToCheckLength - BasePathLength);
DelimiterPos := Pos(DirectorySeparator, sPathToCheck);
// If no delimiter was found or it was found at then end (directories
// may end with it), then the 'sPathToCheck' is in 'sBasePath'.
Result := (DelimiterPos = 0) or (DelimiterPos = PathToCheckLength - BasePathLength);
end;
end
else
Result := False;
end
else
Result := AllowSame and
(((PathToCheckLength = BasePathLength) and
(CompareStr(sPathToCheck, sBasePath) = 0)) or
((PathToCheckLength = BasePathLength - 1) and
(CompareStr(Copy(sBasePath, 1, PathToCheckLength), sPathToCheck) = 0)));
end;
function ExtractDirLevel(const sPrefix, sPath: String): String;
var
PrefixLength: Integer;
begin
if IsInPath(sPrefix, sPath, True, True) then
begin
PrefixLength := Length(sPrefix);
Result := Copy(sPath, 1 + PrefixLength, Length(sPath) - PrefixLength)
end
else
Result := sPath;
end;
function IncludeFrontPathDelimiter(s: String): String;
begin
if (Length(s) > 0) and (s[1] = PathDelim) then
Result:= s
else
Result:= PathDelim + s;
end;
function ExcludeFrontPathDelimiter(s: String): String;
begin
if (Length(s) > 0) and (s[1] = PathDelim) then
Result := Copy(s, 2, Length(s) - 1)
else
Result := s;
end;
function ExcludeBackPathDelimiter(const Path: UTF8String): UTF8String;
var
L: Integer;
begin
L:= Length(Path);
if (L > 1) and (Path[L] in AllowDirectorySeparators) then
Result:= Copy(Path, 1, L - 1)
else
Result:= Path;
end;
procedure DivFileName(const sFileName:String; out n,e:String);
var
i:Integer;
begin
for i:= length(sFileName) downto 1 do
if sFileName[i]='.' then
begin
// if i>1 then // hidden files??
e:=Copy(sFileName,i,Length(sFileName)-i+1);
n:=Copy(sFileName,1,i-1);
Exit;
end;
e:='';
n:=sFileName;
end;
procedure SplitFileMask(const DestMask: String; out DestNameMask: String; out DestExtMask: String);
var
iPos: LongInt;
begin
// Special case for mask that contains '*.*' ('*.*.old' for example)
iPos:= Pos('*.*', DestMask);
if (iPos = 0) then
DivFileName(DestMask, DestNameMask, DestExtMask)
else
begin
DestNameMask := Copy(DestMask, 1, iPos);
DestExtMask := Copy(DestMask, iPos + 1, MaxInt);
end;
// Treat empty mask as '*.*'.
if (DestNameMask = '') and (DestExtMask = '') then
begin
DestNameMask := '*';
DestExtMask := '.*';
end;
end;
function ApplyRenameMask(aFileName: String; NameMask: String; ExtMask: String): String;
function ApplyMask(const TargetString: String; Mask: String): String;
var
i:Integer;
begin
Result:='';
for i:=1 to Length(Mask) do
begin
if Mask[i]= '?' then
Result:=Result + TargetString[i]
else
if Mask[i]= '*' then
Result:=Result + Copy(TargetString, i, Length(TargetString) - i + 1)
else
Result:=Result + Mask[i];
end;
end;
var
sDstExt: String;
sDstName: String;
begin
if ((NameMask = '*') and (ExtMask = '.*')) then
Result := aFileName
else
begin
DivFileName(aFileName, sDstName, sDstExt);
sDstName := ApplyMask(sDstName, NameMask);
sDstExt := ApplyMask(sDstExt, ExtMask);
Result := sDstName;
if sDstExt <> '.' then
Result := Result + sDstExt;
end;
end;
function CharPos(C: Char; const S: string; StartPos: Integer = 1): Integer;
var
sNewStr : String;
begin
if StartPos <> 1 then
begin
sNewStr := Copy(S, StartPos, Length(S) - StartPos + 1);
Result := Pos(C, sNewStr);
if Result <> 0 then
Result := Result + StartPos - 1;
end
else
Result := Pos(C, S);
end;
function NumCountChars(const Char: char; const S: String): Integer;
var
I : Integer;
begin
Result := 0;
if Length(S) > 0 then
for I := 1 to Length(S) do
if S[I] = Char then Inc(Result);
end;
function TrimRightLineEnding(const sText: UTF8String; TextLineBreakStyle: TTextLineBreakStyle): UTF8String;
const
TextLineBreakArray: array[TTextLineBreakStyle] of Integer = (1, 2, 1);
var
I, L: Integer;
begin
L:= Length(sText);
I:= TextLineBreakArray[TextLineBreakStyle];
Result:= Copy(sText, 1, L - I); // Copy without last line ending
end;
function mbCompareText(const s1, s2: UTF8String): PtrInt; inline;
begin
// From 0.9.31 LazUtils can be used but this package does not exist in 0.9.30.
// Result := LazUTF8.UTF8CompareText(s1, s2);
Result := WideCompareText(UTF8Decode(s1), UTF8Decode(s2));
end;
function StrNewW(const mbString: UTF8String): PWideChar;
var
wsString: WideString;
iLength: PtrInt;
begin
Result:= nil;
wsString:= UTF8Decode(mbString);
iLength:= (Length(wsString) * SizeOf(WideChar)) + 1;
Result:= GetMem(iLength);
if Result <> nil then
Move(PWideChar(wsString)^, Result^, iLength);
end;
procedure StrDisposeW(var pStr : PWideChar);
begin
FreeMem(pStr);
pStr := nil;
end;
function StrLCopyW(Dest, Source: PWideChar; MaxLen: SizeInt): PWideChar;
var
I: SizeInt;
begin
Result := Dest;
for I:= 0 to MaxLen - 1 do
begin
if Source^ = #0 then Break;
Dest^ := Source^;
Inc(Source);
Inc(Dest);
end;
Dest^ := #0;
end;
function StrPCopyW(Dest: PWideChar; const Source: WideString): PWideChar;
begin
Result := StrLCopyW(Dest, PWideChar(Source), Length(Source));
end;
function StrPLCopyW(Dest: PWideChar; const Source: WideString; MaxLen: Cardinal): PWideChar;
begin
Result := StrLCopyW(Dest, PWideChar(Source), MaxLen);
end;
function StrBegins(const StringToCheck, StringToMatch: String): Boolean;
begin
Result := (Length(StringToCheck) >= Length(StringToMatch)) and
(CompareChar(StringToCheck[1], StringToMatch[1], Length(StringToMatch)) = 0);
end;
function StrEnds(const StringToCheck, StringToMatch: String): Boolean;
begin
Result := (Length(StringToCheck) >= Length(StringToMatch)) and
(CompareChar(StringToCheck[1 + Length(StringToCheck) - Length(StringToMatch)],
StringToMatch[1], Length(StringToMatch)) = 0);
end;
procedure AddStrWithSep(var SourceString: String; const StringToAdd: String; const Separator: Char);
begin
if (Length(SourceString) > 0) and (Length(StringToAdd) > 0) then
SourceString := SourceString + Separator;
SourceString := SourceString + StringToAdd;
end;
procedure AddStrWithSep(var SourceString: String; const StringToAdd: String; const Separator: String);
begin
if (Length(SourceString) > 0) and (Length(StringToAdd) > 0) then
SourceString := SourceString + Separator;
SourceString := SourceString + StringToAdd;
end;
procedure ParseLineToList(sLine: String; ssItems: TStrings);
var
xPos: Integer;
begin
ssItems.Clear;
while sLine <> '' do
begin
xPos:= Pos(';', sLine);
if xPos > 0 then
begin
ssItems.Add(Copy(sLine, 1, xPos - 1));
Delete(sLine, 1, xPos);
end
else
begin
ssItems.Add(sLine);
Exit;
end;
end;
end;
function ContainsOneOf(StringToCheck: UTF8String; PossibleCharacters: String): Boolean;
var
i, j: SizeInt;
pc : PChar;
begin
pc := @StringToCheck[1];
for i := 1 to Length(StringToCheck) do
begin
for j := 1 to Length(PossibleCharacters) do
if pc^ = PossibleCharacters[j] then
Exit(True);
Inc(pc);
end;
Result := False;
end;
function OctToDec(Value: String): LongInt;
var
I: Integer;
begin
Result:= 0;
for I:= 1 to Length(Value) do
Result:= Result * 8 + StrToInt(Copy(Value, I, 1));
end;
function DecToOct(Value: LongInt): String;
var
iMod: Integer;
begin
Result := '';
while Value >= 8 do
begin
iMod:= Value mod 8;
Value:= Value div 8;
Result:= IntToStr(iMod) + Result;
end;
Result:= IntToStr(Value) + Result;
end;
procedure AddString(var anArray: TDynamicStringArray; const sToAdd: String);
var
Len: Integer;
begin
Len := Length(anArray);
SetLength(anArray, Len + 1);
anArray[Len] := sToAdd;
end;
function SplitString(const S: String; Delimiter: AnsiChar): TDynamicStringArray;
var
Start: Integer = 1;
Len, Finish: Integer;
begin
Len:= Length(S);
for Finish:= 1 to Len - 1 do
begin
if S[Finish] = Delimiter then
begin
AddString(Result, Copy(S, Start, Finish - Start));
Start:= Finish + 1;
end;
end;
if Start <= Len then
begin
AddString(Result, Copy(S, Start, Len - Start + 1));
end;
end;
function ArrBegins(const Array1, Array2: array of String; BothWays: Boolean): Boolean;
var
Len1, Len2: Integer;
i: Integer;
begin
Len1 := Length(Array1);
Len2 := Length(Array2);
if not BothWays and (Len1 < Len2) then
Result := False
else
begin
if Len1 > Len2 then
Len1 := Len2;
for i := 0 to Len1 - 1 do
if Array1[i] <> Array2[i] then
Exit(False);
Result := True;
end;
end;
function ArrayToString(const anArray: TDynamicStringArray; const Separator: Char): String;
var
i: Integer;
begin
Result := '';
for i := Low(anArray) to High(anArray) do
AddStrWithSep(Result, anArray[i], Separator);
end;
function Compare(const Array1, Array2: array of String): Boolean;
var
Len1, Len2: Integer;
i: Integer;
begin
Len1 := Length(Array1);
Len2 := Length(Array2);
if Len1 <> Len2 then
Result := False
else
begin
for i := 0 to Len1 - 1 do
if Array1[i] <> Array2[i] then
Exit(False);
Result := True;
end;
end;
function CopyArray(const anArray: array of String): TDynamicStringArray;
var
i: Integer;
begin
SetLength(Result, Length(anArray));
for i := Low(anArray) to High(anArray) do
Result[i] := anArray[i];
end;
function ContainsOneOf(const ArrayToSearch, StringsToSearch: array of String): Boolean;
var
i: Integer;
begin
for i := Low(StringsToSearch) to High(StringsToSearch) do
if Contains(ArrayToSearch, StringsToSearch[i]) then
Exit(True);
Result := False;
end;
function Contains(const ArrayToSearch: array of String; const StringToSearch: String): Boolean;
var
i: Integer;
begin
for i := Low(ArrayToSearch) to High(ArrayToSearch) do
if ArrayToSearch[i] = StringToSearch then
Exit(True);
Result := False;
end;
procedure DeleteString(var anArray: TDynamicStringArray; const Index: Integer);
var
Len: Integer;
i: Integer;
begin
Len := Length(anArray);
for i := Index + 1 to Len - 1 do
anArray[i - 1] := anArray[i];
SetLength(anArray, Len - 1);
end;
procedure DeleteString(var anArray: TDynamicStringArray; const sToDelete: String);
var
i: Integer;
begin
for i := Low(anArray) to High(anArray) do
if anArray[i] = sToDelete then
begin
DeleteString(anArray, i);
Exit;
end;
end;
function GetArrayFromStrings(Strings: TStrings): TDynamicStringArray;
var
LinesCount: Integer;
i: Integer;
begin
LinesCount := Strings.Count;
if LinesCount > 0 then
begin
if Strings[LinesCount-1] = '' then
Dec(LinesCount);
SetLength(Result, LinesCount);
for i := 0 to LinesCount - 1 do
Result[i] := Strings[i];
end;
end;
procedure SetStringsFromArray(Strings: TStrings; const anArray: TDynamicStringArray);
var
s: String;
begin
Strings.Clear;
for s in anArray do
Strings.Add(s);
end;
procedure SetValue(var anArray: TDynamicStringArray; Key, NewValue: String);
var
i: Integer;
begin
Key := Key + '=';
for i := Low(anArray) to High(anArray) do
if StrBegins(anArray[i], Key) then
begin
anArray[i] := Key + NewValue;
Exit;
end;
AddString(anArray, Key + NewValue);
end;
procedure SetValue(var anArray: TDynamicStringArray; Key: String; NewValue: Boolean);
begin
if NewValue then
SetValue(anArray, Key, 'true')
else
SetValue(anArray, Key, 'false');
end;
function ShortcutsToText(const Shortcuts: TDynamicStringArray): String;
begin
Result := ArrayToString(Shortcuts, ' ');
end;
end.